编辑修复了所有版本中的潜在种族:
1./1 b使用 C++0x中概述的从 (mutex+condition+counter) 构建的信号量没有信号量?如何同步线程?
2.使用“反向”等待来确保信号得到预期工作人员的确认
我真的建议使用 c++11 风格<thread>
来<condition_variable>
实现这一点。
我有两个(半个)示威。他们每个人都假设您有 1 个 master 驱动 10 个 worker。每个工人在工作之前都会等待一个信号。
我们将使用std::condition_variable
(与 a 一起工作std::mutex
)来做信号。第一个版本和第二个版本之间的区别在于信号发送的方式:
- 1. 通知任何工人,一次一个:
- 1乙。使用工人结构
- 2.通知所有线程,协调哪个receiver worker响应
1. 通知任何工人,一次一个:
这是最简单的做法,因为几乎没有协调:
#include <vector>
#include <thread>
#include <mutex>
#include <algorithm>
#include <iostream>
#include <condition_variable>
using namespace std;
class semaphore
{ // see https://stackoverflow.com/questions/4792449/c0x-has-no-semaphores-how-to-synchronize-threads
std::mutex mx;
std::condition_variable cv;
unsigned long count;
public:
semaphore() : count() {}
void notify();
void wait();
};
static void run(int id, struct master& m);
struct master
{
mutable semaphore sem;
master()
{
for (int i = 0; i<10; ++i)
threads.emplace_back(run, i, ref(*this));
}
~master() {
for(auto& th : threads) if (th.joinable()) th.join();
std::cout << "done\n";
}
void drive()
{
// do wakeups
for (unsigned i = 0; i<threads.size(); ++i)
{
this_thread::sleep_for(chrono::milliseconds(rand()%100));
sem.notify();
}
}
private:
vector<thread> threads;
};
static void run(int id, master& m)
{
m.sem.wait();
{
static mutex io_mx;
lock_guard<mutex> lk(io_mx);
cout << "signaled: " << id << "\n";
}
}
int main()
{
master instance;
instance.drive();
}
/// semaphore members
void semaphore::notify()
{
lock_guard<mutex> lk(mx);
++count;
cv.notify_one();
}
void semaphore::wait()
{
unique_lock<mutex> lk(mx);
while(!count)
cv.wait(lk);
--count;
}
1乙。使用工人结构
注意,如果你有一个非静态成员函数的worker
类,你可以做同样的小修改:worker::run
struct worker
{
worker(int id) : id(id) {}
void run(master& m) const;
int id;
};
// ...
struct master
{
// ...
master()
{
for (int i = 0; i<10; ++i)
workers.emplace_back(i);
for (auto& w: workers)
threads.emplace_back(&worker::run, ref(w), ref(*this));
}
// ...
void worker::run(master& m) const
{
m.sem.wait();
{
static mutex io_mx;
lock_guard<mutex> lk(io_mx);
cout << "signaled: " << id << "\n";
}
}
一个警告
cv.wait()
可能会遭受虚假唤醒,其中条件变量实际上没有被提高(例如,在操作系统信号处理程序的情况下)。这是任何平台上的条件变量都会发生的常见事情。
以下方法解决了这个问题:
2.通知所有线程,协调哪个receiver worker
使用标志来指示哪个线程打算接收信号:
struct master
{
mutable mutex mx;
mutable condition_variable cv;
int signaled_id; // ADDED
master() : signaled_id(-1)
{
让我们假设它driver
变得更有趣,并希望以特定(随机...)顺序向所有工作人员发出信号:
void drive()
{
// generate random wakeup order
vector<int> wakeups(10);
iota(begin(wakeups), end(wakeups), 0);
random_shuffle(begin(wakeups), end(wakeups));
// do wakeups
for (int id : wakeups)
{
this_thread::sleep_for(chrono::milliseconds(rand()%1000));
signal(id);
}
}
private:
void signal(int id) // ADDED id
{
unique_lock<mutex> lk(mx);
std::cout << "signaling " << id << "\n";
signaled_id = id; // ADDED put it in the shared field
cv.notify_all();
cv.wait(lk, [&] { return signaled_id == -1; });
}
现在我们要做的就是确保接收线程检查它的 id 是否匹配:
m.cv.wait(lk, [&] { return m.signaled_id == id; });
m.signaled_id = -1;
m.cv.notify_all();
这结束了虚假唤醒。
完整的代码清单/现场演示: