C ++ 11线程:等待条件变量的多个线程

时间:2013-07-02 07:29:47

标签: c++ multithreading c++11 mutex condition-variable

我目前正在研究一个模拟扩展的Producer-Worker模型的问题。在这个问题中,有3个工人和3个工具可供使用,而对于工人来说,他们需要2个工具(和材料,但那些是无关紧要的)。如果保险库中有> = 2个工具,则工作人员将采用2.否则,他们将等待条件变量,当存在> = 2时将发出信号。

对于2名工人来说这很好:一个工作然后将工具返回到金库,另一个等待工人将被唤醒并使用2个工具。问题是,有3名工人,总会有一个人渴望得到这些工具。

经过一些测试后,我注意到等待条件变量的线程以堆栈形式构建。有没有可能使它排队? (1等待,2等待,3等待。当1被唤醒并想要制造另一个时,他必须等待2和3后面。)

这是一个示例输出。代码太长,所以如果真的有必要,我会发布它。有3个工作线程和1个工具互斥锁。任何挨饿的人都不同于其他人。

1 Tools taken. Remaining: 1
2 Waiting on tools...
3 Waiting on tools...
1 Operator Product made. Tools returned. Tools now:3
3 Tools taken. Remaining: 1
1 Waiting on tools...
3 Materials returned for switch.
3 Operator Product made. Tools returned. Tools now:3
1 Tools taken. Remaining: 1
3 Waiting on tools...
1 Materials returned for switch.
1 Operator Product made. Tools returned. Tools now:3
3 Tools taken. Remaining: 1
1 Waiting on tools...
3 Materials returned for switch.
3 Operator Product made. Tools returned. Tools now:3
1 Tools taken. Remaining: 1
3 Waiting on tools...
1 Materials returned for switch.
1 Operator Product made. Tools returned. Tools now:3
3 Tools taken. Remaining: 1
1 Waiting on tools...
3 Materials returned for switch.
3 Operator Product made. Tools returned. Tools now:3
1 Tools taken. Remaining: 1
3 Waiting on tools...
1 Materials returned for switch.
...

(正如你所看到的,2永远不会得到工具......)

更新:2013/07/05 我添加了一些代码。

int tools = 3; //global
string last; //current last product on output buffer
mutex toolsMutex;
mutex matSearchMutex;

int main(){
//Initializing Producers
    Producer prod1(1);
    Producer prod2(2);
        Producer prod3(3);



    thread p1(processor,1);
    thread p2(processor,2);
    thread p3(processor,3);

    p1.detach();
    p2.detach();
    p3.detach();

    while(true){//forever running

    }

    return 0;
}

处理器:

  //Processor method
void processor(int i){
    srand(time(NULL)); 

    while (true){ //forever running


    bool hasTools = false;
    bool productMade = false;
    while (productMade == false){ //while product has yet to be made.
        //choose what to make...



        if (hasTools == false){
            thread matT(getMaterials,whatToMake);
            thread toolT(getTools,i);
            toolT.join();           
            matT.join();
            hasTools = true;
        }
        else{ //tools acquired but no materials
            thread matT(getMaterials,whatToMake);
            matT.join();
        }

        if (recordedLast.compare(last) != 0){

            //return materials and acquire new ones the next run

            continue;
        }
        else {
            makeProduct(whatToMake);
            unique_lock<mutex> locker(toolMutex); 
            tools = tools + 2;
            cout << i << " Operator Product made. Tools returned. Tools now:" << tools << endl;
            productMade = true;
            if (tools >=2)  toolsCV.notify_one();
        }

    //done processing

    }


}   

}

makeProducts:

void makeProduct(int i){
    unique_lock<mutex> mainMatLock(matSearchMutex); 
    // make product according to i
    this_thread::sleep_for(chrono::milliseconds(rand() % 1000 + 10));   
}

GETTOOLS:

void getTools(int i){
    unique_lock<mutex> locker(toolMutex); 
    if (tools <2){
        cout << i << " Waiting on tools..." << endl;
        toolsCV.wait(locker);}
    tools = tools - 2;//tools acquired
    cout << i <<" Tools taken. Remaining: " << tools << endl;

}

感谢那些回复的人。我将尝试使用多个条件变量来实现等待队列。

(P.S。有什么更好的方法可以在Stack Overflow上进行代码格式化吗?除了四个空格......

4 个答案:

答案 0 :(得分:10)

当您致电std::condition_variable时,

notify_one未指定唤醒哪个等待线程。因此,您应该编写不关心哪个线程被唤醒的代码。标准模式是无论哪个线程被唤醒,该线程都应该完成需要完成的工作。

如果您要求以特定顺序唤醒线程,请使用其他机制。例如,您可以为每个线程分别使用std::condition_variable,然后在需要工具时将线程放入队列中。当一个线程交给工具时,它可以发信号通知对应于队列前面的线程的条件变量。然后该线程将被唤醒,其他线程将保持睡眠状态(模拟虚假唤醒)。

答案 1 :(得分:3)

当有多个线程等待条件时,顺序 他们被唤醒(notify_all)或哪一个被唤醒 唤醒(notify_one)未指定。如果你需要某种形式 在排序方面,您需要使用notify_all并实施它 你自己。您可以保持线程队列等待:之前 等待(但在获取互斥锁之后),将线程ID推入 队列的结尾。在循环中,循环“此线程在 队列前面和必要的工具“。当你得到了 工具,从队列前面删除id,然后调用 再次notify_all

答案 2 :(得分:1)

这里真正的问题是,如果你有工作线程和有限的所需资源,你不应该关心哪些线程实际被激活,你应该只关心工作完成。这里唯一的区别是日志记录。您定义的线程数是可并行运行的线程数,受到资源限制为一个。

如果这对您不合适,那么您需要按照其他答案中的说明自行采取行动。

答案 3 :(得分:0)

一种方法可能是使用在线程之间共享的完全成熟的信号量而不是条件变量。这样,您可以等待特定的计数。

#include <mutex>
#include <thread>
#include <condition_variable>

using std::mutex;
using std::condition_variable;

class Semaphore
{
public:
    /**
     * Construct a counting semaphore with an initial value
     * @param cnt The value of the initial semaphore count
     */
    Semaphore(unsigned int cnt);

    /**
     * acquire a semaphore count
     * @param numRes The number of count ressources to acquire
     */
    void acquire(unsigned int numRes = 1);

    /**
     * try to acquire a semaphore count.
     * @param numRes The number of count ressources that the method tries to acquire
     * @return true, if numRes could be aquired
     *         false, otherwise
     */
    bool tryAcquire(unsigned int numRes = 1);

    /**
     * release one semaphore cnt
     * @param numRes The number of count ressources to release
     */
    void release(unsigned int numRes = 1);

private:
    unsigned int cnt;
    mutex mut;
    condition_variable cond;
};

实施如下:

void Semaphore::acquire(unsigned int numRes)
{
    unique_lock<mutex> lock(mut);
    while (cnt < numRes)
    {
        cond.wait(lock);
    }

    cnt-=numRes;
}

bool Semaphore::tryAcquire(unsigned int numRes)
{
    unique_lock<mutex> lock(mut);
    if (cnt>=numRes)
    {
        cnt -= numRes;
        return true;
    }
    return false;
}

void Semaphore::release(unsigned int numRes)
{
    {
        unique_lock<mutex> lock(mut);
        cnt += numRes;
    }
    // notify <numRes> waiting entities
    for (unsigned int i = 0; i<numRes; ++i)
    {
        cond.notify_one(); 
    }
}