正确等待多个线程

时间:2016-07-20 18:24:49

标签: c++ multithreading c++11 concurrency

我正在尝试使用C ++ 11并发支持。

我有一种工作线程的线程池,它们都做同样的事情,其中​​主线程有一个条件变量数组(每个线程一个,它们需要'开始'同步,即不能提前一个周期运行他们的循环)。

    for (auto &worker_cond : cond_arr) {
        worker_cond.notify_one();
    }

然后该线程必须等待池的每个线程的通知再次重新启动它的循环。这样做的正确方法是什么?有一个条件变量并等待某个整数,每个不是主的线程会增加吗?类似的东西(仍在主线程中)

    unique_lock<std::mutex> lock(workers_mtx);
    workers_finished.wait(lock, [&workers] { return workers = cond_arr.size(); });

2 个答案:

答案 0 :(得分:1)

我在这里看到两个选项:

选项1:join()

基本上,不是使用条件变量来开始线程中的计算,而是为每次迭代生成一个新线程,并使用join()等待它完成。然后为下一次迭代生成新线程,依此类推。

选项2:锁定

只要其中一个线程仍在工作,您就不希望主线程通知。因此每个线程都有自己的锁,在进行计算之前锁定它,然后解锁。在调用notify()之前,您的主线程将锁定所有这些线程并在之后将其解锁。

答案 1 :(得分:1)

我认为你的解决方案没有根本错误。

使用workers保护workers_mtx并完成。

我们可以用计数信号量来抽象它。

struct counting_semaphore {
  std::unique_ptr<std::mutex> m=std::make_unique<std::mutex>();
  std::ptrdiff_t count = 0;
  std::unique_ptr<std::condition_variable> cv=std::make_unique<std::condition_variable>();

  counting_semaphore( std::ptrdiff_t c=0 ):count(c) {}
  counting_semaphore(counting_semaphore&&)=default;

  void take(std::size_t n = 1) {
    std::unique_lock<std::mutex> lock(*m);
    cv->wait(lock, [&]{ if (count-std::ptrdiff_t(n) < 0) return false; count-=n; return true; } );
  }
  void give(std::size_t n = 1) {
    {
      std::unique_lock<std::mutex> lock(*m);
      count += n;
      if (count <= 0) return;
    }
    cv->notify_all();
  }
};

take需要count,如果没有足够的话会阻止。

give添加到count,并通知是否有正数。

现在,工作人员在两个信号量之间传递令牌。

std::vector< counting_semaphore > m_worker_start{count};
counting_semaphore m_worker_done{0}; // not count, zero
std::atomic<bool> m_shutdown = false;

// master controller:
for (each step) {
  for (auto&& starts:m_worker_start)
    starts.give();
  m_worker_done.take(count);
}

// master shutdown:
m_shutdown = true;
// wake up forever:
for (auto&& starts:m_worker_start)
  starts.give(std::size_t(-1)/2);

// worker thread:
while (true) {
  master->m_worker_start[my_id].take();
  if (master->m_shutdown) return;
  // do work
  master->m_worker_done.give();
}

或某些。

live example