std :: condition_variable多次调用notify_all

时间:2016-02-10 15:15:49

标签: c++ multithreading mutex condition-variable

首先,让我向您介绍我的问题。

我的代码如下所示:

#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?

2 个答案:

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