跳转至

信号量

counting_semaphore 是一个轻量同步元件,能控制对共享资源的访问。不同于 std::mutex,counting_semaphore 允许同一资源有多于一个同时访问,至少允许 LeastMaxValue 个同时的访问者。

1 定义

  • counting_semaphore:实现非负资源计数的信号量
  • binary_semaphore:仅拥有二个状态的信号量, LeastMaxValue 等于 1,也就是内存的 counter 只有 0 和 1 两种状态。
namespace std {
  template<ptrdiff_t LeastMaxValue = /* 实现定义 */>
    class counting_semaphore;
 
  using binary_semaphore = counting_semaphore<1>;
}

类模板

namespace std {
  template<ptrdiff_t LeastMaxValue = /* 实现定义 */>
  class counting_semaphore {
  public:
    static constexpr ptrdiff_t max() noexcept;
 
    constexpr explicit counting_semaphore(ptrdiff_t desired);
    ~counting_semaphore();
 
    counting_semaphore(const counting_semaphore&) = delete;
    counting_semaphore& operator=(const counting_semaphore&) = delete;
 
    void release(ptrdiff_t update = 1);
    void acquire();
    bool try_acquire() noexcept;
    template<class Rep, class Period>
      bool try_acquire_for(const chrono::duration<Rep, Period>& rel_time);
    template<class Clock, class Duration>
      bool try_acquire_until(const chrono::time_point<Clock, Duration>& abs_time);
 
  private:
    ptrdiff_t counter;          // 仅用于阐释
  };
}

2 方法

  • release:增加内部计数器并解锁获取者
  • acquire:减少内部计数器或阻塞到直至能如此
  • try_acquire:尝试减少内部计数器而不阻塞
  • try_acquire_for:尝试减少内部计数器,至多阻塞一段时长
  • try_acquire_until:尝试减少内部计数器,阻塞直至一个时间点

3 示例

#include <iostream>
#include <thread>
#include <chrono>
#include <semaphore>
using namespace std::literals; 

// 全局二元信号量实例
// 设置对象计数为零
// 对象在未被发信状态
std::binary_semaphore smphSignal(0);

void ThreadProc()
{
    // 通过尝试减少信号量的计数等待来自主程序的信号
    smphSignal.acquire();

    // 此调用阻塞直至信号量的计数被从主程序增加

    std::cout << "[thread] Got the signal" << std::endl; // 回应消息

    // 等待 3 秒以模仿某种线程正在进行的工作
    std::this_thread::sleep_for(3s);

    std::cout << "[thread] Send the signal\n"; // 消息

    // 对主程序回复发信
    smphSignal.release();
}

int main()
{
    // 创建某个背景工作线程,它将长期存在
    std::jthread thrWorker(ThreadProc);

    std::cout << "[main] Send the signal\n"; // 消息

    // 通过增加信号量的计数对工作线程发信以开始工作
    smphSignal.release();

    // release() 后随 acquire() 可以阻止工作线程获取信号量,所以添加延迟:
    std::this_thread::sleep_for(50ms);

    // 通过试图减少信号量的计数等待直至工作线程完成工作
    smphSignal.acquire();

    std::cout << "[main] Got the signal\n"; // 回应消息
}

输出:

[main] Send the signal
[thread] Got the signal
[thread] Send the signal
[main] Got the signal