信号量

ZaynPei Lv6

信号量(Semaphore)是一种用于多线程或多进程编程中的同步机制,主要用于控制对共享资源的访问。它可以用来防止资源竞争和确保线程安全, 一般可以实现为一个特殊的整数计数器,用于管理对有限资源的访问。它代表了可用资源的数量

  • 当计数值大于0时,表示有可用的资源。
  • 当计数值等于0时,表示没有可用的资源。

信号量的基本操作

信号量主要有两个基本操作:P(Proberen,测试)和V(Verhogen,增加),也称为 wait 和 signal 操作。

P操作的核心逻辑是:尝试获取一个资源

  • 检查资源:检查信号量(count)的值。

  • 分配资源:如果信号量的值大于0(count > 0),意味着有可用资源。此时,操作会成功,并将信号量的值减1(count–),表示一个资源已被占用。线程可以继续执行。

  • 等待资源:如果信号量的值等于0(count == 0),意味着没有可用资源。此时,请求资源的线程必须阻塞(即暂停执行),进入等待队列,直到有其他线程释放资源。`

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// P操作 - 等待信号量,获取资源
void wait()
{
// 1. 加锁:为了保证对 count 的检查和修改是原子操作,防止竞态条件。
std::unique_lock<std::mutex> lock(mtx);

// 2. 检查资源并等待:
// 使用 while 循环而不是 if 是为了防止“虚假唤醒”(spurious wakeup), 即线程被唤醒后,条件仍然不满足的情况。
// 当 count == 0 时,表示没有资源可用。
while (count == 0) {
// 3. 阻塞线程:
// cv.wait(lock) 会原子地完成两件事:
// a. 释放互斥锁 lock。
// b. 将当前线程置于等待状态。
// 当线程被唤醒后,它会重新获取锁,并再次检查 while 的条件。
cv.wait(lock); // 这里的cv是一个条件变量
}

// 4. 获取资源:
// 当线程跳出循环时,说明 count > 0,有可用资源。
// 将 count 减一,表示成功获取了一个资源。
count--;
}

V操作的核心逻辑是:释放一个资源

  • 增加资源计数:将信号量的值加1(count++),表示一个资源已被释放,现在可用。

  • 唤醒等待者:如果此时有其他线程因为等待该资源而被阻塞,V操作会唤醒其中一个等待的线程,让它可以尝试再次获取资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// V操作 - 释放信号量,释放资源
void signal()
{
// 1. 加锁:同样是为了保证对 count 的修改和通知操作的原子性。
std::unique_lock<std::mutex> lock(mtx);

// 2. 释放资源:
// 将 count 加一,表示一个资源被释放了。
count++;

// 3. 唤醒等待的线程:
// cv.notify_one() 会唤醒一个(如果有的话)正在 cv.wait() 上等待的线程。
// 被唤醒的线程会从 wait() 函数中醒来,尝试重新获取锁并继续执行。
cv.notify_one();
}
On this page
信号量