Skip to content

Commit 920f1ae

Browse files
committed
完成第四章的“C++20 信号量”一节所有内容 #12
1 parent 00b226d commit 920f1ae

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed

md/04同步操作.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,57 @@ int main() {
11771177

11781178
信号量常用于发信/提醒而非互斥,通过初始化该信号量为 0 从而阻塞尝试 acquire() 的接收者,直至提醒者通过调用 release(n) “发信”。在此方面可把信号量当作**条件变量的替代品****通常它有更好的性能**
11791179

1180+
假设我们有一个 Web 服务器,它只能处理有限数量的并发请求。为了防止服务器过载,我们可以**使用信号量来限制并发请求的数量**
1181+
1182+
```cpp
1183+
// 定义一个信号量,最大并发数为 3
1184+
std::counting_semaphore<3> semaphore{ 3 };
1185+
1186+
void handle_request(int request_id) {
1187+
// 请求到达,尝试获取信号量
1188+
std::cout << "进入 handle_request 尝试获取信号量\n";
1189+
1190+
semaphore.acquire();
1191+
1192+
std::cout << "成功获取信号量\n";
1193+
1194+
// 此处延时三秒可以方便测试,会看到先输出 3 个“成功获取信号量”,因为只有三个线程能成功调用 acquire,剩余的会被阻塞
1195+
std::this_thread::sleep_for(3s);
1196+
1197+
// 模拟处理时间
1198+
std::random_device rd;
1199+
std::mt19937 gen{ rd() };
1200+
std::uniform_int_distribution<> dis(1, 5);
1201+
int processing_time = dis(gen);
1202+
std::this_thread::sleep_for(std::chrono::seconds(processing_time));
1203+
1204+
std::cout << std::format("请求 {} 已被处理\n", request_id);
1205+
1206+
semaphore.release();
1207+
}
1208+
1209+
int main() {
1210+
// 模拟 10 个并发请求
1211+
std::vector<std::jthread> threads;
1212+
for (int i = 0; i < 10; ++i) {
1213+
threads.emplace_back(handle_request, i);
1214+
}
1215+
}
1216+
```
1217+
1218+
> [运行](https://godbolt.org/z/xs9asvqre)测试。
1219+
1220+
这段代码很简单,以至于我们可以在这里来再说一条概念:
1221+
1222+
- `counting_semaphore` 是一个轻量同步原语,能控制对共享资源的访问。不同于 [std::mutex](https://zh.cppreference.com/w/cpp/thread/mutex)`counting_semaphore` **允许同一资源进行多个并发的访问**,至少允许 `LeastMaxValue` 个同时的访问者。
1223+
- `binary_semaphore``std::counting_semaphore` 的特化的别名,其 `LeastMaxValue` 为 1。
1224+
1225+
`LeastMaxValue` 是我们设置的非类型模板参数,意思是信号量维护的计数最大值。我们这段代码设置的是 `3`,也就是允许 3 个同时访问者。事实上我们的代码就是这样做的。
1226+
1227+
牢记信号量的基本的概念不变,计数的值不能小于 `0`,如果当前信号量的计数值为 `0`,那么执行“***等待***”(acquire)操作的线程将会**一直阻塞**。明白这点,那么就都不存在问题。
1228+
1229+
通过这种方式,可以有效控制 Web 服务器处理并发请求的数量,防止服务器过载。
1230+
11801231
[^4]:注:**如果信号量只有二进制的 0 或 1,称为二进制信号量(binary semaphore)**,这就是这个类型名字的由来。
11811232

11821233
## 总结

0 commit comments

Comments
 (0)