互斥量、信号量、条件变量,三者之间的互相实现
这三者是多任务编程中最常见的基础设施,而它们背后隐藏着更深层的关系。例如,它们全都可以用信号量来实现;如果没有信号量,也可以用互斥量和条件变量组合实现一个信号量……
namespace bigcat {
class mutex {
private:
semaphore sem = semaphore(1);
public:
void wait() {
sem.wait();
}
void notify() {
sem.notify();
}
};
class semaphore {
private:
condition_variable cv;
mutex mtx;
unsigned int count;
public:
semaphore(unsigned int n) : count(n) {};
void wait() {
mtx.lock();
while(!count) cv.wait(mtx);
count--;
mtx.unlock();
}
void notify() {
mtx.lock();
count++;
mtx.unlock();
cv.notify_all();
}
};
class condition_variable {
private:
semaphore sem = semaphore(0);
mutex mtx_count;
unsigned int wait_count = 0;
public:
void wait(mutex &mtx) {
mtx.unlock();
mtx_count.lock();
wait_count++;
mtx_count.unlock();
sem.wait();
mtx.lock();
}
void notify() {
mtx_count.lock();
if(wait_count) {
wait_count--;
}
sem.notify();
mtx_count.unlock();
}
void notify_all() {
mtx_count.lock();
for(; wait_count; wait_count--) {
sem.notify();
}
mtx_count.unlock();
}
};
}
值一提的是:C++20 以前(不包含 C++20)的线程相关库并不支持信号量,于是可以通过这种形式来实现。最简单快捷的方法还是直接使用系统的 API,但这会降低可移植性,需要做出一定取舍。
- 信号量→互斥量
- 互斥量+信号量→条件变量
- 条件变量+互斥量→信号量
如果只允许操作系统实现三种其中一种,那么显然单独信号量就足以实现其他两种设施。这种意义上信号量确实是最基础的设施。信号量从直观上是一个计数器加上一个“休眠本任务/唤醒其它任务”的机制。它是系统内核中的任务调度控制机构在用户层的最简单体现。