【问题标题】:How to wait for multiple threads to finish (with c++11 threads)?如何等待多个线程完成(使用 c++11 线程)?
【发布时间】:2018-01-02 19:47:43
【问题描述】:

我正在尝试编写一个程序,它使用 c++11 线程功能来生成多个线程,主线程必须等待每个生成的线程完成,并且所有生成的线程必须并行运行。我想出了以下方法:

#include <iostream>
#include <stdio.h>
#include <thread>
#include <condition_variable>
#include <mutex>

using namespace std;

class Producer
{
public:
    Producer(int a_id):
        m_id(a_id),
        m_running(false),
        m_ready(false),
        m_terminate(false)
    {
        m_id = a_id;
        m_thread = thread(&Producer::run, this);
        while (!m_ready) {}
    }

    ~Producer() {
        terminate();
        m_thread.join();
    }

    void wait() {
        unique_lock<mutex> lock(m_waitForRunFinishMutex);
        m_cond.wait(lock);
        // avoid spurious wake up
        if (m_running) {
            wait();
        }
        lock.unlock();
        cout << "wait exit " << m_id << endl;
    }

    void start() {
        m_running = true;
        m_cond.notify_all();
    }

    void terminate() {
        start();
        m_terminate = true;
    }

    void run() {
        m_ready = true;
        do {
            unique_lock<mutex> lock(m_mutex);
            while (!m_running) {
                m_cond.wait(lock);
            }

            if (!m_terminate) {
                cout << "running thread: " << m_id << endl;
            }

            m_running = false;
            m_cond.notify_all();
        } while (!m_terminate);
    }

private:
    int m_id;
    bool m_running;
    bool m_ready;
    bool m_terminate;
    thread m_thread;
    mutex m_mutex;
    mutex m_waitForRunFinishMutex;
    condition_variable m_cond;
};

只用一个线程测试时程序运行良好,即以下程序:

int main()
{
    Producer producer1(1);    
    producer1.start();
    producer1.wait();

    return 0;
}

结果如下:

running thread: 1
wait exit: 1

但是,如果我用 2 个线程测试程序,例如:

int main()
{
    Producer producer1(1);
    Producer producer2(2);

    producer1.start();
    producer2.start();

    producer1.wait();
    producer2.wait();

    return 0;
}

我得到以下输出:

running thread: 2
running thread: 1
wait exit 1

似乎 producer2 永远不会收到通知(在producer2.wait()),因此程序永远不会结束。希望有人能指出我在这里缺少的东西。

感谢大家帮助解决问题。最终,问题的根本原因在已接受答案的第 (3) 点中描述。我通过更正wait 函数解决了这个问题,如下所示:

void wait() {
    unique_lock<mutex> lock(m_waitForRunFinishMutex);
    while (m_running) {
        m_cond.wait(lock);
    }
    lock.unlock();
}

【问题讨论】:

  • 不应该wait() 产生死锁吗?因为它被称为递归并且unique_lock&lt;mutex&gt; lock(m_waitForRunFinishMutex) 被调用,而m_waitForRunFinishMutex 仍然被锁定。
  • 一个递归调用从一个方法返回,这意味着 m_waitForRunFinishMutex 在递归调用时被解锁(即因为它超出了范围)。
  • @Gio 你对第一部分有误;递归函数无需展开即可添加到堆栈中。
  • 递归调用真的会导致范围离开吗?这是奇怪的行为。不过谢谢。
  • 是的,变量离开了作用域,即函数返回。见这里:stackoverflow.com/questions/5852237/…,这意味着 m_waitForRunFinishMutex 在递归调用时被解锁。

标签: c++ multithreading


【解决方案1】:

这里是一目了然的问题的快速集合。

  1. wait() 是递归的,没有解锁它的唯一锁(根据 Detonar 的评论)

  2. while (!m_ready) {} 不在内存屏障中(尝试通过一些优化进行编译,看看会发生什么!)

  3. 如果工作线程在调用 wait() 之前完成;在等待条件变量之前不进行检查。由于工作线程已完成;它永远不会被唤醒。显然,在等待条件变量之前,您必须检查线程是否可以在互斥锁中被唤醒。

【讨论】:

  • 第 3 点有什么问题?必须循环调用条件变量以避免虚假唤醒,对吗?例如stackoverflow.com/questions/15072479/…
  • 如果你得到虚假唤醒,那么你的逻辑是错误的;根据spurious 的定义。但是,如果您要醒来说,因为您是在每个任务完成后才醒来,而不仅仅是在线程完成时;那么您的评论应该反映为什么在每项任务中醒来都是您的意图。
  • 我没有遇到任何虚假唤醒,但是操作系统可能会唤醒线程,因此最好防止虚假唤醒,请参阅en.wikipedia.org/wiki/Spurious_wakeup
  • 关于第 (2) 点,我看不出这里有什么问题。变量 m_ready 的唯一用途是确保在函数调用 start() 时,线程确实存在,c++ 没有为此提供任何方法,因此可以很好地检查它。 m_ready 没有在不同的线程中修改,因此我看不到任何保护它的用途。
  • 关于(1),我也看不出这里有什么问题,m_waitForRunFinishMutex 在递归调用时被解锁。另外我刚刚添加了一个跟踪来检查这是否可能是问题,但事实并非如此,没有发生wait 的递归调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-18
  • 1970-01-01
  • 1970-01-01
  • 2019-07-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多