使用 condition_variable::notify_all 通知多个线程
Posted
技术标签:
【中文标题】使用 condition_variable::notify_all 通知多个线程【英文标题】:Using condition_variable::notify_all to notify multiple threads 【发布时间】:2018-06-24 13:41:21 【问题描述】:我一直在尝试编写餐饮哲学家的代码,以便更好地使用多线程编程。在我的代码中,我有一个condition_variable
,它会停止线程,直到创建了所有线程。但是,似乎当我调用condition_variable::notify_all
通知所有线程都已创建并开始“吃”时,只通知一个线程。例如:
我有一个 Philosophers 类,它有这些成员变量:
static std::condition_variable start;
static std::mutex start_mutex;
还有这些成员函数。
static void start_eating()
start.notify_all();
void dine()
signal(SIGINT, ctrl_c_catch);
std::unique_lock lk start_mutex ;
start.wait(lk);
std::cout << id << "started\n";
// see end for complete class...
每个线程都在 condition_variable start
上等待,并且在我调用 start_eating()
之前不会继续。问题是当我调用start.notify_all();
时,只有一个线程得到通知并继续。但是,当我在等待后更改代码以解锁互斥锁时,一切运行正常(所有线程继续):
std::unique_lock lk start_mutex ;
start.wait(lk);
lk.unlock();
我不明白这里发生了什么。为什么需要解锁互斥锁?
完整代码:
#include <chrono>
#include <mutex>
#include <vector>
#include <thread>
#include <condition_variable>
#include <atomic>
#include <signal.h>
#include <iostream>
#include <shared_mutex>
#include <ctime>
namespace clk = std::chrono;
const auto EAT_SLEEP_TIME = clk::milliseconds1; // 5 seconds
const auto NUM_SEATS = 5U;
using Fork = std::mutex; // is the fork being used or not
std::mutex cout_mutex;
void ctrl_c_catch(int dummy);
class Philosopher
Fork& left;
Fork& right;
unsigned id;
unsigned times_eaten;
static std::condition_variable start;
static std::mutex start_mutex;
static std::atomic_bool end;
public:
Philosopher(Fork& l, Fork& r, unsigned i) : left l , right r , id i , times_eaten
static void start_eating()
start.notify_all();
static void stop_eating()
end = true;
void dine()
signal(SIGINT, ctrl_c_catch);
std::unique_lock lk start_mutex ;
start.wait(lk);
// lk.unlock(); // uncommenting this fixes the issue
std::cout << id << " started\n";
while (!end)
if (&right < &left)
right.lock();
left.lock();
else
left.lock();
right.lock();
cout_mutex.lock();
std::clog << id << " got both forks, eating\n";
cout_mutex.unlock();
++times_eaten;
std::this_thread::sleep_for(EAT_SLEEP_TIME * (rand() % 50));
right.unlock();
left.unlock();
std::this_thread::sleep_for(EAT_SLEEP_TIME * (rand() % 50));
cout_mutex.lock();
std::cout << id << " stopped, terminating thread. Eaten " << times_eaten << "\n";
cout_mutex.unlock();
delete this;
;
std::atomic_bool Philosopher::end = false;
std::condition_variable Philosopher::start;
std::mutex Philosopher::start_mutex;
template <size_t N, typename T = unsigned>
constexpr std::array<T, N> range(T b = 0, T s = 1)
std::array<T, N> ret;
for (auto& i : ret)
i = b;
b += s;
return ret;
void ctrl_c_catch(int dummy)
std::cout << "Caught ctrl-c or stop\nStoping Philosophers\n";
Philosopher::stop_eating();
std::this_thread::sleep_for(clk::seconds5);
exit(0);
int main()
srand(time(NULL));
signal(SIGINT, ctrl_c_catch);
std::vector<Fork> forks NUM_SEATS ; // 5 forks
std::vector<std::thread> phil; // vector of philosophers
for (unsigned i : range<NUM_SEATS - 1>())
auto p = new Philosopherforks[i], forks[i + 1], i;
phil.emplace_back(&Philosopher::dine, p);
auto p = new Philosopherforks[NUM_SEATS - 1], forks[0], NUM_SEATS - 1;
phil.emplace_back(&Philosopher::dine, p);
std::clog << "Waiting for 5 seconds\n";
std::this_thread::sleep_for(clk::seconds10);
std::clog << "Starting Philosophers\n Type 'stop' to stop\n";
Philosopher::start_eating();
for (auto& t : phil)
t.detach();
std::this_thread::sleep_for(clk::seconds15);
ctrl_c_catch(0);
std::string dummy;
std::cin >> dummy;
if (dummy == "stop")
ctrl_c_catch(0);
return 0;
【问题讨论】:
【参考方案1】:正如here 解释的那样,调用std::condition_variable::wait
会释放锁,等待,唤醒后重新获取锁。所以你需要手动解锁(或者自动使用RAII),让其他线程锁定它。 C++ 中的条件变量与非阻塞监视器具有相似的语义,因此您可以阅读它。此外,由于不可避免的虚假解除阻塞,您应该使用另一个版本的函数,即使用谓词的函数(更多信息在上面的链接中)。
【讨论】:
以上是关于使用 condition_variable::notify_all 通知多个线程的主要内容,如果未能解决你的问题,请参考以下文章
在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?
Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)