C++ 使用notify in循环时,wait中的谓词仅调用一次

C++ 使用notify in循环时,wait中的谓词仅调用一次,c++,multithreading,c++11,C++,Multithreading,C++11,在一个线程中对循环中的条件变量调用notify_once方法时,另一个线程中的wait方法似乎只检查它一次谓词。示例代码如下所示: int someNumber = 0; mutex numberMutex; condition_variable cv; void waitFunction() { unique_lock<mutex> lock(numberMutex); auto duration = chrono::seconds(5); // Predicate

在一个线程中对循环中的条件变量调用
notify_once
方法时,另一个线程中的
wait
方法似乎只检查它一次谓词。示例代码如下所示:

int someNumber = 0;
mutex numberMutex;
condition_variable cv;

void waitFunction()
{
  unique_lock<mutex> lock(numberMutex);
  auto duration = chrono::seconds(5);

  // Predicate is only called once?
  cv.wait_for(lock, duration, []() {
    cout << "Checking condition: 10 == " << someNumber << '\n';
    return someNumber == 10;
  });
  cout << "Done with this thread...\n" << flush;
}

int main()
{
  thread waiter(waitFunction);

  for (size_t number = 0; number != 50; ++number)
  {
    {
      lock_guard<mutex> guard(numberMutex);
      someNumber = number;
    }
    cv.notify_one();
  }

  waiter.join();
}
而我希望它在每次迭代时检查条件


这怎么可能呢?

信令线程不会等待接收到信号

这意味着在主循环重复并再次递增之前,等待的线程可能没有机会醒来

您有一个“普通”竞争条件(虽然不是语言级别的数据竞争,所以没有UB)

这里有一个反例。根据平台的速度和日程安排,您可以选择以下选项:

#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <thread>

using namespace std;

int someNumber = 0;
mutex numberMutex;
condition_variable cv;

void waitFunction() {
    unique_lock<mutex> lock(numberMutex);
    auto duration = chrono::seconds(5);

    // Predicate is only called once?
    cv.wait_for(lock, duration, []() {
        cout << "Checking condition: 10 == " << someNumber << '\n';
        return someNumber == 10;
    });
    cout << "Done with this thread...\n" << flush;
}

int main() {
    thread waiter(waitFunction);

    for (size_t number = 0; number != 50; ++number) {
        this_thread::sleep_for(chrono::milliseconds(1));
        {
            lock_guard<mutex> guard(numberMutex);
            someNumber = number;
            std::cout << "notif: " << number  << "\n";
            cv.notify_one();
        }
    }

    waiter.join();
}
根本原因

如果需要双向通信,请设置一个标志来回传主线程(例如使用不同的条件变量)

作为一个旁注,考虑在互斥体下发送CV以改进调度保证。
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <thread>

using namespace std;

int someNumber = 0;
mutex numberMutex;
condition_variable cv;

void waitFunction() {
    unique_lock<mutex> lock(numberMutex);
    auto duration = chrono::seconds(5);

    // Predicate is only called once?
    cv.wait_for(lock, duration, []() {
        cout << "Checking condition: 10 == " << someNumber << '\n';
        return someNumber == 10;
    });
    cout << "Done with this thread...\n" << flush;
}

int main() {
    thread waiter(waitFunction);

    for (size_t number = 0; number != 50; ++number) {
        this_thread::sleep_for(chrono::milliseconds(1));
        {
            lock_guard<mutex> guard(numberMutex);
            someNumber = number;
            std::cout << "notif: " << number  << "\n";
            cv.notify_one();
        }
    }

    waiter.join();
}
Checking condition: 10 == 0
notif: 0
Checking condition: 10 == 0
notif: 1
Checking condition: 10 == 1
notif: 2
Checking condition: 10 == 2
notif: 3
Checking condition: 10 == 3
notif: 4
Checking condition: 10 == 4
notif: 5
Checking condition: 10 == 5
notif: 6
Checking condition: 10 == 6
notif: 7
Checking condition: 10 == 7
notif: 8
Checking condition: 10 == 8
notif: 9
Checking condition: 10 == 9
notif: 10
Checking condition: 10 == 10
Done with this thread...
notif: 11
notif: 12
notif: 13
notif: 14
notif: 15
notif: 16
notif: 17
notif: 18
notif: 19
notif: 20
notif: 21
notif: 22
notif: 23
notif: 24
notif: 25
notif: 26
notif: 27
notif: 28
notif: 29
notif: 30
notif: 31
notif: 32
notif: 33
notif: 34
notif: 35
notif: 36
notif: 37
notif: 38
notif: 39
notif: 40
notif: 41
notif: 42
notif: 43
notif: 44
notif: 45
notif: 46
notif: 47
notif: 48
notif: 49