【C++并发编程】(八)信号量

(八)信号量

信号量(Semaphore)是一种同步机制,通过维护一个计数来控制多个线程对共享资源的访问。信号量的计数值是一个非负整数,表示可用资源的数量。当线程或进程需要访问共享资源时,它会尝试获取信号量。如果信号量的计数值大于0,则将其减1并允许线程或进程继续执行;如果信号量的计数值为0,则线程或进程将被阻塞,直到信号量的计数值大于0。这样的信号量也称为计数信号量

除了计数信号量之外,还有二值信号量。二值信号量只有两个状态(0 和 1),类似于互斥锁,主要用于控制单个资源的访问。计数值为1表示资源可用,计数值为0表示资源被占用。

信号量可以用于线程间的同步,使得某些操作按特定顺序执行,从而避免竞态条件的发生,也可以限制对共享资源的并发访问数量,以防止资源过载。

C++20 标准引入了 std::counting_semaphorestd::binary_semaphore 类,分别用于实现计数信号量和二值信号量的功能。下面以餐馆中的用餐预订作类比,介绍C++中信号量的使用。

C++ 计数信号量

假设有一个餐馆,由于桌子数量有限,顾客需要提前预订桌子才能用餐。为了有效管理这些预订,店主可以采用类似于计数信号量的系统来确保桌子的合理分配和预订管理。

计数信号量对象可以通过std::counting_semaphore<最大计数值> 信号量对象(初始计数值) 创建,并指定其最大计数值(桌子数量)和初始计数值(空闲的桌子数量)。 信号量对象的主要成员函数包括:

  • acquire():用于请求信号量。如果计数值大于 0(表示有资源空闲) ,计数值减 1 并立即返回,线程获得对资源的访问权;如果计数值为 0(表示资源都已被占用),则阻塞线程直到信号量变为可用状态(即计数值大于0)。类比:当顾客来预订桌子时,系统检查还有没有空桌子。如果有(计数量不为0),系统会减少一个计数,并告诉顾客预订成功。如果所有桌子都被预订了(计数为0),系统会告诉顾客需要等待,直到有桌子空出来。
  • release():用于释放信号量,增加计数值。类比:当顾客用餐完毕离开时,店主会增加一个计数,表示有一张桌子空出来了,可以让等待的顾客进来用餐。
#include <semaphore>
#include <thread>
#include <iostream>
#include <mutex>
#include <string>
#include <format>
std::counting_semaphore<10> tables(10);
std::mutex cout_mutex;

void customer(int id) {
    tables.acquire(); // 线程请求信号量 (顾客预订桌子)

    std::cout << std::format("Customer {} got a table.\n", id);   
    
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 用餐

    std::cout << std::format("Customer {} finished dining.\n", id);   

    tables.release(); // 线程释放信号量(顾客用餐完毕,离开桌子)
}

int main() {
    std::thread customers[15];
    for (int i = 0; i < 15; ++i) {
        customers[i] = std::thread(customer, i + 1);
    }
    for (int i = 0; i < 15; ++i) {
        customers[i].join();
    }
    return 0;
}
Customer 7 got a table.
Customer 11 got a table.
Customer 14 got a table.
Customer 10 got a table.
Customer 12 got a table.
Customer 8 got a table.
Customer 13 got a table.
Customer 1 got a table.
Customer 9 got a table.
Customer 15 got a table.
Customer 7 finished dining.
Customer 10 finished dining.
Customer 12 finished dining.
Customer 14 finished dining.
Customer 5 got a table.
Customer 13 finished dining.
Customer 2 got a table.
Customer 6 got a table.
Customer 1 finished dining.
Customer 3 got a table.
Customer 11 finished dining.
Customer 15 finished dining.
Customer 8 finished dining.
Customer 4 got a table.
Customer 9 finished dining.
Customer 4 finished dining.
Customer 3 finished dining.
Customer 5 finished dining.
Customer 6 finished dining.
Customer 2 finished dining.

当所有10个桌子都被预订时,其他顾客需要等待,直到有桌子空出来。

C++ 二值信号量

假设这家餐馆还有一个VIP包间,店主则可以采用类似于二值信号量的系统对其进行预定管理。

二值信号量对象可以通过std::binary_semaphore 信号量对象(初始计数值) 创建,其计数值为1,表示VIP包间空闲;计数值为0则表示包间被占用。信号量对象的成员函数主要有:

  • acquire():用于请求信号量。如果信号量的计数值为1(表示资源空闲),计数值减1(变成0)并立即返回,线程获得对资源的访问权。如果信号量的计数值为0(表示资源已被占用),则阻塞线程直到信号量变为可用状态(即计数值变为1)。类比:当一个VIP顾客预订包间时,系统检查包间是否空闲。如果空闲(即信号量计数为1),他会将计数减少(变成0),表示包间已被该顾客占用。如果包间已被预订(即信号量计数为0),系统会告诉顾客需要等待,直到包间再次变得空闲(即信号量计数变回1)。
  • release():释放信号量。类比:当VIP顾客用餐完毕离开时,系统会增加计数(变成1),表示包间空出来了,可以让下一个VIP顾客使用。
#include <semaphore>
#include <thread>
#include <iostream>

std::binary_semaphore vip_room(1); // 1个VIP包间,初始计数值为1

void Customer(int id) {
    vip_room.acquire(); // 预订VIP包间
    std::cout << "VIP Customer " << id << " got the VIP room.\n";
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 用餐
    std::cout << "VIP Customer " << id << " finished dining.\n";
    vip_room.release(); // 用餐完毕,离开包间
}

int main() {
    std::thread Customers[3];
    for (int i = 0; i < 3; ++i) {
        Customers[i] = std::thread(Customer, i + 1);
    }
    for (int i = 0; i < 3; ++i) {
        Customers[i].join();
    }
    return 0;
}
VIP Customer 2 got the VIP room.
VIP Customer 2 finished dining.
VIP Customer 1 got the VIP room.
VIP Customer 1 finished dining.
VIP Customer 3 got the VIP room.
VIP Customer 3 finished dining.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二进制人工智能

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值