首先,让我向您介绍我的问题。
我的代码如下所示:
#include <iostream>
#include <thread>
#include <condition_variable>
std::mutex mtx;
std::mutex cvMtx;
std::mutex mtx2;
bool ready{false};
std::condition_variable cv;
int threadsFinishedCurrentLevel{0};
void tfunc() {
for(int i = 0; i < 5; i++) {
//do something
for (int j = 0; j < 10000; j++) {
std::cout << j << std::endl;
}
//this is i-th level
mtx2.lock();
threadsFinishedCurrentLevel++;
if (threadsFinishedCurrentLevel == 2) {
//this is last thread in current level
threadsFinishedCurrentLevel = 0;
cvMtx.unlock();
}
mtx2.unlock();
{
//wait for notify
unique_lock<mutex> lck(mtx);
while (!ready) cv_.wait(lck);
}
}
}
int main() {
cvMtx.lock(); //init
std::thread t1(tfunc);
std::thread t2(tfunc);
for (int i = 0; i < 5; i++) {
cvMtx.lock();
{
unique_lock<mutex> lck(mtx);
ready = true;
cv.notify_all();
}
}
t1.join();
t2.join();
return 0;
}
我有2个帖子。我的计算包括级别(对于这个例子,假设我们有5个级别)。在同一级别上,计算可以分为线程。然后每个线程计算一部分问题。当我想要进入下一个(更高)级别时,必须首先完成较低级别。所以我的想法是这样的。当完成当前级别的最后一个线程时,它会解锁主线程,因此它可以通知所有线程继续下一级别。但是这个通知必须被调用一次。因为这些水平很多。可以重启这个condition_variable还是什么?或者我需要每个级别一个condition_variable?例如,当我有1000个级别时,我需要动态分配1000x condition_variable?
答案 0 :(得分:1)
是仅仅是我还是你试图用互斥锁阻塞主线程(这是你在完成所有线程时尝试通知它的方式?),我的意思是这不是互斥体的任务。这就是应该使用条件变量的地方。
Memory=[1,2,3,4,5,6,27]
Network =[7,8,9,10,11,12,13,14]
Processes =[15,16,17,18,19,20,21,22,23,24,29]
CPU=[30,31,31,33,34,35]
System=[]
答案 1 :(得分:0)
可以使用单个计数器完成同步,线程在锁定下递增计数器并检查计数器是否达到并发线程数的倍数。这极大地简化了逻辑。我做了这个更改,并将共享变量分组到一个类中,并提供了访问它们的成员函数。为了避免false sharing,我确保只读变量与线程读写的变量分开,并且按使用情况分开读写变量。不鼓励使用全局变量,请参阅C++ Core Guidelines以获取此建议和其他好建议。
简化代码如下,你可以看到它live in ideone。注意:看起来在ideone中没有真正的并发性,你必须在多核环境中运行它来实际测试硬件并发性。
//http://stackoverflow.com/questions/35318942/stdcondition-variable-calling-notify-all-more-than-once
#include <iostream>
#include <functional>
#include <thread>
#include <mutex>
#include <vector>
#include <condition_variable>
static constexpr size_t CACHE_LINE_SIZE = 64;
static constexpr size_t NTHREADS = 2;
static constexpr size_t NLEVELS = 5;
static constexpr size_t NITERATIONS = 100;
class Synchronize
{
alignas(CACHE_LINE_SIZE) // read/write while threads are busy working
std::mutex mtx_std_cout;
alignas(CACHE_LINE_SIZE) // read/write while threads are synchronizing at level
std::mutex cvMtx;
std::condition_variable cv;
size_t threadsFinished{0};
alignas(CACHE_LINE_SIZE) // read-only parameters
const size_t n_threads;
const size_t n_levels;
public: // class Synchronize owns unique resources:
// - must be explicitly constructed
// - disallow default ctor,
// - disallow copy/move ctor and
// - disallow copy/move assignment
Synchronize( Synchronize const& ) = delete;
Synchronize & operator=( Synchronize const& ) = delete;
explicit Synchronize( size_t nthreads, size_t nlevels )
: n_threads{nthreads}, n_levels{nlevels}
{}
size_t nlevels() const { return n_levels; }
std::mutex & std_cout_mutex() { return mtx_std_cout; }
void level_done_wait_all( size_t level )
{
std::unique_lock<std::mutex> lk(cvMtx);
threadsFinished++;
cv.wait(lk, [&]{return threadsFinished >= n_threads * (level+1);});
cv.notify_all();
}
};
void tfunc( Synchronize & sync )
{
for(size_t i = 0; i < sync.nlevels(); i++)
{
//do something
for (size_t j = 0; j < NITERATIONS; j++) {
std::unique_lock<std::mutex> lck(sync.std_cout_mutex());
if (j == 0) std::cout << '\n';
std::cout << ' ' << i << ',' << j;
}
sync.level_done_wait_all(i);
}
}
int main() {
Synchronize sync{ NTHREADS, NLEVELS };
std::vector<std::thread*> threads(NTHREADS,nullptr);
for(auto&t:threads) t = new std::thread(tfunc,std::ref(sync));
for(auto t:threads) {
t->join();
delete t;
}
std::cout << std::endl;
return 0;
}