使用 condition_variable::notify_all 通知多个线程
Using condition_variable::notify_all to notify multiple threads
我一直在尝试编写餐饮哲学家的代码,作为通过多线程编程变得更好的一种方式。在我的代码中,我有一个condition_variable
,它会停止线程,直到创建完所有线程。但是,似乎当我调用condition_variable::notify_all
通知所有线程都已创建并开始"吃"时,只通知了一个线程。例如:
我有一个哲学家类,它有这些成员变量:
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 << "startedn";
// 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::milliseconds{1}; // 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 << " startedn";
while (!end) {
if (&right < &left) {
right.lock();
left.lock();
} else {
left.lock();
right.lock();
}
cout_mutex.lock();
std::clog << id << " got both forks, eatingn";
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 stopnStoping Philosophersn";
Philosopher::stop_eating();
std::this_thread::sleep_for(clk::seconds{5});
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 Philosopher{forks[i], forks[i + 1], i};
phil.emplace_back(&Philosopher::dine, p);
}
auto p = new Philosopher{forks[NUM_SEATS - 1], forks[0], NUM_SEATS - 1};
phil.emplace_back(&Philosopher::dine, p);
std::clog << "Waiting for 5 secondsn";
std::this_thread::sleep_for(clk::seconds{10});
std::clog << "Starting Philosophersn Type 'stop' to stopn";
Philosopher::start_eating();
for (auto& t : phil)
t.detach();
std::this_thread::sleep_for(clk::seconds{15});
ctrl_c_catch(0);
std::string dummy;
std::cin >> dummy;
if (dummy == "stop")
ctrl_c_catch(0);
return 0;
}
如此处所述,调用std::condition_variable::wait
释放锁,等待,唤醒后,重新获取锁。因此,您需要手动(或使用 RAII 自动(解锁它以允许其他线程锁定它。C++中的条件变量与非阻塞监视器具有类似的语义,因此您可以阅读它以获得更好的直观理解。此外,由于无法阻止的虚假解锁,您应该使用该函数的其他版本,即使用谓词的版本(更多信息在上面的链接中(。
相关文章:
- 是否有必要获取锁并在不需要唤醒线程时通知condition_variable?
- C++ Poco - 如何向特定线程发送通知/消息?
- C++ - 如何不错过来自多个线程的多个通知?
- C++ - std::condition_variable 由不同的线程通知
- 线程启动延迟 - 通知所有未唤醒所有线程
- 使用 condition_variable::notify_all 通知多个线程
- 通知所有在多线程C++不起作用.导致死锁
- 通知线程是否始终需要在修改期间锁定共享数据
- 当子线程退出时,如何通知父线程
- QSocketNotifier:不能从另一个线程启用或禁用套接字通知程序
- 使用Berkeley套接字的线程通知
- 通知有关变量变化的线程(信号?)
- 用信号通知线程启动特定的函数
- 线程退出时通知Waiters
- C++11 std::condition_variable:我们可以将锁直接传递给通知的线程吗?
- 通知线程退出
- std::condition_variable - 等待多个线程通知观察者
- 线程之间的更改通知标志需要内存屏障吗
- 视觉提升 条件变量中的同步队列C++不通知其他线程上的等待类方法
- c++主线程通知线程通知主线程