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,但这会降低可移植性,需要做出一定取舍。

  • 信号量→互斥量
  • 互斥量+信号量→条件变量
  • 条件变量+互斥量→信号量

如果只允许操作系统实现三种其中一种,那么显然单独信号量就足以实现其他两种设施。这种意义上信号量确实是最基础的设施。信号量从直观上是一个计数器加上一个“将本任务阻塞/解除其它任务阻塞”的机制。它是系统内核中的任务调度控制机构在用户层的最简单体现。