当前位置:首页 > C++ > 正文

C++信号量详解(手把手教你实现多线程同步)

C++并发编程 中,多个线程同时访问共享资源时,很容易引发数据竞争和不一致的问题。为了解决这个问题,我们需要使用同步机制,而 C++信号量 就是其中一种非常有效的工具。

本文将从零开始,详细讲解 信号量使用教程,即使你是编程小白,也能轻松掌握如何在 C++ 中使用信号量来实现 多线程同步

什么是信号量?

信号量(Semaphore)是一种用于控制多个线程对共享资源访问的同步原语。它内部维护一个计数器,表示当前可用资源的数量:

  • 当线程请求资源时,信号量计数器减1;
  • 当线程释放资源时,信号量计数器加1;
  • 如果计数器为0,后续请求将被阻塞,直到有资源被释放。
C++信号量详解(手把手教你实现多线程同步) C++信号量 多线程同步 C++并发编程 信号量使用教程 第1张

C++ 中如何使用信号量?

C++ 标准库在 C++20 之前并没有直接提供信号量支持,但我们可以借助 POSIX 信号量(Linux/macOS)或 Windows API(Windows)来实现。不过,从 C++20 开始,标准库引入了 <semaphore> 头文件,使得跨平台使用信号量变得更加简单。

C++20 信号量基本用法

下面是一个使用 C++20 std::counting_semaphore 的简单示例:

#include <iostream>#include <thread>#include <semaphore>#include <vector>std::counting_semaphore<10> sem(1); // 初始计数为1,最多10个void worker(int id) {    sem.acquire(); // 获取信号量(计数-1)    std::cout << "线程 " << id << " 正在访问临界区\n";    std::this_thread::sleep_for(std::chrono::milliseconds(500));    std::cout << "线程 " << id << " 退出临界区\n";    sem.release(); // 释放信号量(计数+1)}int main() {    std::vector<std::thread> threads;    for (int i = 0; i < 5; ++i) {        threads.emplace_back(worker, i);    }    for (auto& t : threads) {        t.join();    }    return 0;}

在这个例子中,我们创建了一个初始值为1的计数信号量,这意味着同一时间只能有一个线程进入临界区(即打印语句之间的代码)。这实际上起到了互斥锁(mutex)的作用。

二值信号量 vs 计数信号量

  • 二值信号量:计数器只能是0或1,功能类似于互斥锁,用于保护临界区。
  • 计数信号量:计数器可以大于1,用于限制同时访问某资源的线程数量(例如连接池、缓冲区等)。

实际应用场景

信号量常用于以下场景:

  • 生产者-消费者问题(控制缓冲区大小)
  • 限制数据库连接池中的并发连接数
  • 控制对有限硬件资源的访问(如打印机、GPU)

注意事项

  • 确保每个 acquire() 都有对应的 release(),否则会导致死锁。
  • C++20 的 std::counting_semaphore 是无所有权的,不像 mutex 那样必须由获取它的线程释放。
  • 如果你使用的是 C++17 或更早版本,可以考虑使用 std::condition_variable + std::mutex 模拟信号量,或者使用平台特定的 API。

总结

通过本篇 信号量使用教程,你应该已经掌握了 C++ 中信号量的基本概念和使用方法。无论是在处理 多线程同步 问题,还是在构建高性能并发程序时,C++信号量 都是一个强大而灵活的工具。

记住:合理使用同步原语是编写安全、高效并发程序的关键。希望这篇关于 C++并发编程 的入门指南能为你打下坚实的基础!