我正在尝试使用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(); });
答案 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();
}
或某些。