C++ 如何同步在不同线程上运行的函数实例(在c+;+;11中)?

C++ 如何同步在不同线程上运行的函数实例(在c+;+;11中)?,c++,multithreading,synchronization,mutex,condition-variable,C++,Multithreading,Synchronization,Mutex,Condition Variable,假设有许多线程由运行相同函数实例的循环组成,但是每个迭代的开始都需要同步(因此首先完成的线程必须等待最后一个线程开始新的迭代)。如何在c++11中实现这一点 这篇文章的其余部分正是我尝试过的,以及它是如何失败的 我正在使用一个计数器“sync”,最初设置为3(线程数)。在函数结束时,每个线程都将从该计数器中减去1并开始等待。当计数器达到0时,这意味着其中3个已经完成了一轮,因此主线程将计数器重置为3,并通知线程唤醒它们 这在大多数情况下都有效,但有时一个或两个线程无法唤醒 这些是全局变量: m

假设有许多线程由运行相同函数实例的循环组成,但是每个迭代的开始都需要同步(因此首先完成的线程必须等待最后一个线程开始新的迭代)。如何在c++11中实现这一点

这篇文章的其余部分正是我尝试过的,以及它是如何失败的

我正在使用一个计数器“sync”,最初设置为3(线程数)。在函数结束时,每个线程都将从该计数器中减去1并开始等待。当计数器达到0时,这意味着其中3个已经完成了一轮,因此主线程将计数器重置为3,并通知线程唤醒它们

这在大多数情况下都有效,但有时一个或两个线程无法唤醒

这些是全局变量:

mutex syncMutex;
condition_variable syncCV;
int sync;
这是在线程中循环运行的函数的末尾:

unique_lock<mutex> lk(syncMutex);
cout << "Thread num: " << mFieldNum << " got sync value: " << sync;
sync --;
syncCV.notify_all();
cout << " and goes to sleep..." << endl;
syncCV.wait(lk, []{return sync == numFields;});
cout << "Thread num: " << mFieldNum << " woke up" << endl;
}

有人有线索吗?谢谢你的阅读

线程同步存在许多问题。托尼在评论中提到了一个。在主循环代码中还有一个潜在的争用条件,即在调用syncCV.notify_all()之前调用lk.unlock()。(这可能会使线程错过notify_all信号。)

我会用两种方式调整你的代码。首先,为了解决使用“sync==numFields”作为条件的问题,正如Tony所指出的,在另一个线程执行了sync之后,这个条件可能不成立,因此可以将每个线程在每个主线程循环中只运行一次作为条件。在我的示例代码中,这是通过引入“done[numFields]”变量实现的。其次,引入两个条件变量是有意义的——一个用于通知工作线程新的主循环迭代已经开始,另一个用于通知主线程工作线程已经完成。(请注意,两个条件变量使用相同的互斥锁。)

下面是一个完整的程序,以您的示例代码为模型,它结合了以下两种方法:

#include <iostream>
using std::cout;
using std::endl;

#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>

std::mutex syncMutex;
std::condition_variable readyCV;
std::condition_variable doneCV;
int sync;
bool exitFlag;

const int numFields = 5;
bool done[numFields];

const int nloops = 10;

void thread_func(int i) {
  int mFieldNum = i;
  while (true) {
    std::unique_lock<std::mutex> lk(syncMutex);
    readyCV.wait(lk, [mFieldNum]{return  exitFlag || !done[mFieldNum-1];});
    if (exitFlag)  break;
    cout << "Thread num: " << mFieldNum << " woke up, got sync value: " << sync;
    if (--sync == 0)  doneCV.notify_all();
    done[mFieldNum-1] = true;
    readyCV.notify_all();
    cout << " and goes to sleep..." << endl;
  }
}

int main (int argc, char* argv[]) {
  exitFlag = false;
  sync = 0;
  std::vector<std::thread> threads;
  for (int i = 0; i < numFields; i++) {
    done[i] = true;
    threads.emplace_back (thread_func, i+1);
  }
  for (int i = 0; i <= nloops; i++) {
    std::unique_lock<std::mutex> lk(syncMutex);
    doneCV.wait(lk, []{return sync == 0;});
    cout << "main loop (lk held), i = " << i << endl;
    sync = numFields;
    if (i == nloops)  exitFlag = true;
    else              for (auto &b : done)  b = false;
    cout << "Notifying all threads!" << endl;
    readyCV.notify_all();
  }

  for (auto& t : threads)  t.join();
}
#包括
使用std::cout;
使用std::endl;
#包括
#包括
#包括
#包括
std::mutex syncMutex;
std::条件变量readyCV;
std::条件变量doneCV;
整数同步;
bool-exitFlag;
常量int numFields=5;
bool done[numFields];
常数int nloops=10;
无效线程函数(int i){
int-mFieldNum=i;
while(true){
std::unique_lock lk(syncMutex);
readyCV.wait(lk[mFieldNum]{return exitFlag | | |!done[mFieldNum-1];});
如果(exitFlag)中断;

cout因为每个线程都在循环中运行,所以在线程1或2唤醒后,在线程3[]{return sync==numFields;}之前执行了sync--谓词已执行。谓词的计算结果为false,因此线程3没有唤醒。谢谢@TonyJ您知道如何解决此问题吗?谢谢!这是我在这里提出的第一个问题,您的帮助确实很好地说明了这个社区。经过数小时的挫折,我也找到了一个类似于您的解决方案。我将sync变量设置为a向量,这样每个线程将通过将它们在向量中的位置设置为零来指示它已完成,然后主线程将等待看到所有零重置。
Thread num: 1 got sync value: 3 and goes to sleep...
Thread num: 2 got sync value: 2 and goes to sleep...
Thread num: 3 got sync value: 1 and goes to sleep...
Notifying all threads!
Thread num: 1 woke up
Thread num: 2 woke up
Thread num: 3 woke up
Thread num: 2 got sync value: 3 and goes to sleep...
Thread num: 1 got sync value: 2 and goes to sleep...
Thread num: 3 got sync value: 1 and goes to sleep...
Notifying all threads!
Thread num: 2 woke up
Thread num: 1 woke up
Thread num: 2 got sync value: 3 and goes to sleep...
Thread num: 1 got sync value: 2 and goes to sleep...
#include <iostream>
using std::cout;
using std::endl;

#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>

std::mutex syncMutex;
std::condition_variable readyCV;
std::condition_variable doneCV;
int sync;
bool exitFlag;

const int numFields = 5;
bool done[numFields];

const int nloops = 10;

void thread_func(int i) {
  int mFieldNum = i;
  while (true) {
    std::unique_lock<std::mutex> lk(syncMutex);
    readyCV.wait(lk, [mFieldNum]{return  exitFlag || !done[mFieldNum-1];});
    if (exitFlag)  break;
    cout << "Thread num: " << mFieldNum << " woke up, got sync value: " << sync;
    if (--sync == 0)  doneCV.notify_all();
    done[mFieldNum-1] = true;
    readyCV.notify_all();
    cout << " and goes to sleep..." << endl;
  }
}

int main (int argc, char* argv[]) {
  exitFlag = false;
  sync = 0;
  std::vector<std::thread> threads;
  for (int i = 0; i < numFields; i++) {
    done[i] = true;
    threads.emplace_back (thread_func, i+1);
  }
  for (int i = 0; i <= nloops; i++) {
    std::unique_lock<std::mutex> lk(syncMutex);
    doneCV.wait(lk, []{return sync == 0;});
    cout << "main loop (lk held), i = " << i << endl;
    sync = numFields;
    if (i == nloops)  exitFlag = true;
    else              for (auto &b : done)  b = false;
    cout << "Notifying all threads!" << endl;
    readyCV.notify_all();
  }

  for (auto& t : threads)  t.join();
}
#include <iostream>
using std::cout;
using std::endl;

#include <atomic>
#include <mutex>
#include <thread>
#include <vector>

std::mutex coutMutex;

std::atomic<int> sync;

const int numFields = 5;
bool done[numFields];

const int nloops = 10;

void thread_func(int i) {
  int mFieldNum = i;
  int mySync = sync--;
  {
    std::lock_guard<std::mutex> lk(coutMutex);
    cout << "Thread num: " << mFieldNum << " woke up, got sync value: " << mySync << endl;
  }
}  

int main (int argc, char* argv[]) {
  for (int i = 0; i < nloops; i++) {
    cout << "main loop, i = " << i << endl;
    std::vector<std::thread> threads;
    sync = numFields;
    for (int i = 0; i < numFields; i++)  threads.emplace_back (thread_func, i+1);
    for (auto& t : threads)  t.join();
  }
}