C++20中的counting_semaphore的应用

一、std::counting_semaphore

在前面介绍过C++20中的同步库,其中就提到过std::counting_semaphore。但当时的重点是同步库的整体介绍,本文则会对std::counting_semaphore这个信号量进行一个全面的分析和说明,并有针对性的给出具体的例程。
C++20中对其的定义为:

template< std::ptrdiff_t LeastMaxValue = /* implementation-defined */ >
class counting_semaphore;
using binary_semaphore = std::counting_semaphore<1>;

LeastMaxValue值并不实际最大值,即此值是可以被突破的。

二、应用说明

要想掌握好std::counting_semaphore,如果能够在学习操作系统时将其中的PV原语理解的很透彻,那么这就不是什么问题了。所以推荐大家如果不清楚的可以回头先看一看什么是PV原语。
std::counting_semaphore作为一个轻量级的同步原语,主要是用来对共享资源的控制,与互斥体等的不同,其允许对同一资源进行指定数量的线程同步访问。怎么说呢,其实就是C++标准中的同步控制,在以前基本只能同一情况下访问同一资源(其它库如Posix,windows平台等)。可能大牛们觉得已经够用了,但在其它语言都推出了信号灯及其类似的同步原语,而且随着C++应用的不断发展,应该是大牛们觉得还是要提供一下类似的实现(这只是个人猜测)。
counting_semaphore提供了一个指定线程同步访问的数量,而binary_semaphore则可以理解成前者的一个特化例子,只支持0,1两种状态,有点类似于互斥体,但它没有所有权一说。这样应用到一些特定的场景时不用调用复杂的counting_semaphore。
std::counting_semaphore与普通的Mutex一个重要的不同在于,与线程的所有权控制不同,前者不会绑定到指定的线程,也即任何一个线程都可以进行获取和释放信号量,请开发者一定要明白。
其最典型的两个接口为:
1、std::counting_semaphore::release
原子性的按Update增加内部计数器,即增加信号量。当然update的值需要大于等0且小于等于最大值-当前计数器大小。
2、std::counting_semaphore::acquire
原子性的将内部计数器减1,前提是内部计数器大于0;否则将阻塞线程,直到计数器超过0。

这里有一个需要注意的情况,counting_semaphore在信号的下限即0时,调用acquire,不会出现异常问题;但在到达上限时,再调用release,则会出现UB行为。这点非常重要。
如果大家想扩展一下视野,还可以看看其它库或平台中的类似的semaphore的实现,也可以看看其它语言(如Java等)中类似的实现,就可以更好的理解信号量的应用。

三、例程

看一下cppreference上的例程:

#include <chrono>
#include <iostream>
#include <semaphore>
#include <thread>

// global binary semaphore instances
// object counts are set to zero
// objects are in non-signaled state
std::binary_semaphore
    smphSignalMainToThread{0},
    smphSignalThreadToMain{0};

void ThreadProc()
{
    // wait for a signal from the main proc
    // by attempting to decrement the semaphore
    smphSignalMainToThread.acquire();

    // this call blocks until the semaphore's count
    // is increased from the main proc

    std::cout << "[thread] Got the signal\n"; // response message

    // wait for 3 seconds to imitate some work
    // being done by the thread
    using namespace std::literals;
    std::this_thread::sleep_for(3s);

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

    // signal the main proc back
    smphSignalThreadToMain.release();
}

int main()
{
    // create some worker thread
    std::thread thrWorker(ThreadProc);

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

    // signal the worker thread to start working
    // by increasing the semaphore's count
    smphSignalMainToThread.release();

    // wait until the worker thread is done doing the work
    // by attempting to decrement the semaphore's count
    smphSignalThreadToMain.acquire();

    std::cout << "[main] Got the signal\n"; // response message
    thrWorker.join();
}

运行结果:

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

四、总结

大牛陈硕曾经说过,实际的编程场景基本用不到semaphore,如果真用到了,极有可能设计上有问题。他的这个说法本人非常赞同。建议在实际场景中尽量还是谨慎使用这种信号量,不过,在某些特定的场景下,如果确实是需要使用信号量,或者整体考虑代价要小于其它方法的情况下,也不是不可以使用。
仍然是前面反复提到的,合适的就是最好的。技术本身没有绝对的好与坏!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值