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

新手上路,请多包涵

我目前正在研究一个模拟扩展 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

        }
    }
}

制作产品:

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

获取工具:

 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;
}

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

(PS 有没有更好的方法在 Stack Overflow 上进行代码格式化?除了四个空格…

原文由 user1745746 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 586
2 个回答

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

如果您要求以特定顺序唤醒线程,则使用不同的机制。例如,您可以为每个线程设置一个单独的 std::condition_variable ,然后在需要工具时将线程放入队列中。当一个线程提交工具时,它可以向队列前面的线程对应的条件变量发出信号。然后该线程将被唤醒,而其他线程将保持睡眠状态(模数虚假唤醒)。

原文由 Anthony Williams 发布,翻译遵循 CC BY-SA 3.0 许可协议

当有多个线程等待一个条件时,它们被唤醒的顺序( notify_all )或哪个线程被唤醒( notify_one )是未指定的。如果您需要某种排序,则需要使用 notify_all 并自己实现。您可以保留一个等待线程的队列:在等待之前(但在获取互斥体之后),将线程 id 推送到队列的末尾。在循环中,循环“队列前面的这个线程和可用的必要工具”。拿到工具后,把队列前面的id去掉,再调用 notify_all

原文由 James Kanze 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏