1.概要
2.内容
在C++中,唤醒指定的线程通常涉及使用条件变量(std::condition_variable
)或信号量(如std::semaphore
,C++20引入)等同步机制。这些机制允许一个线程等待(阻塞)另一个线程的通知(唤醒)。
以下是一个使用std::condition_variable
来唤醒指定线程的示例:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void worker_thread() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready; });
std::cout << "Worker thread has been woken up!" << std::endl;
}
void notifier_thread() {
std::this_thread::sleep_for(std::chrono::seconds(2)); // Simulate some work
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one();
}
int main() {
std::thread worker(worker_thread);
std::thread notifier(notifier_thread);
worker.join();
notifier.join();
return 0;
}
在这个示例中:
worker_thread
函数中的线程会等待条件变量cv
。notifier_thread
函数中的线程模拟一些工作(通过std::this_thread::sleep_for
等待2秒),然后设置ready
为true
并通知等待的线程(cv.notify_one()
)。- 当
worker_thread
被唤醒后,它会打印一条消息。
使用 std::semaphore
如果你使用的是C++20或更新的标准,你也可以使用 std::semaphore
来实现相同的功能。以下是一个使用 std::semaphore
的示例:
#include <iostream>
#include <thread>
#include <semaphore>
#include <chrono>
std::semaphore sem(0);
void worker_thread() {
sem.acquire();
std::cout << "Worker thread has been woken up!" << std::endl;
}
void notifier_thread() {
std::this_thread::sleep_for(std::chrono::seconds(2)); // Simulate some work
sem.release();
}
int main() {
std::thread worker(worker_thread);
std::thread notifier(notifier_thread);
worker.join();
notifier.join();
return 0;
}
在这个示例中:
worker_thread
函数中的线程会等待信号量sem
。notifier_thread
函数中的线程模拟一些工作(通过std::this_thread::sleep_for
等待2秒),然后释放信号量(sem.release()
)。- 当
worker_thread
被唤醒后,它会打印一条消息。
这两种方法都可以有效地唤醒指定的线程,具体使用哪种方式取决于你的使用场景和C++标准版本。
3.关联知识
1.std::semaphore
std::semaphore
是 C++20 标准库中新增的一个同步原语,用于管理多个线程对共享资源的访问。它允许你控制同时访问某个特定资源或代码段的线程数量。std::semaphore
提供了一种机制,通过计数器来跟踪当前有多少线程可以进入临界区(即访问共享资源的代码段)。
基本概念
- 计数(Count):
std::semaphore
的核心是一个计数器,它表示当前还可以有多少线程进入临界区。当计数器的值为 0 时,表示没有更多的线程可以进入临界区,直到有其他线程离开并释放信号量,计数器的值才会增加。 - 等待(Wait):当一个线程想要进入临界区时,它会尝试“等待”信号量。如果信号量的计数器大于 0,则线程可以进入临界区,并且计数器的值会减 1。如果计数器的值为 0,则线程会被阻塞,直到有其他线程释放信号量。
- 通知(Signal):当一个线程完成临界区的操作后,它会“通知”信号量,即增加计数器的值。这可能会唤醒一个正在等待进入临界区的线程。
主要成员函数
- 构造函数:
std::semaphore(unsigned int count);
创建一个信号量,初始计数为count
。 - wait:
void wait();
尝试减少信号量的计数。如果计数为 0,则线程会被阻塞直到计数大于 0。 - try_wait:
bool try_wait();
尝试减少信号量的计数。如果计数大于 0,则减少并返回true
;如果计数为 0,则立即返回false
而不阻塞线程。 - notify:
void notify();
增加信号量的计数。如果有线程因为计数为 0 而被阻塞,则唤醒其中一个线程。
使用场景
std::semaphore
非常适合用于控制对资源的并发访问,比如限制同时访问某个数据库连接的线程数量,或者限制同时执行某个特定任务的线程数量。
示例代码
#include <iostream>
#include <semaphore>
#include <thread>
#include <vector>
std::semaphore sem(3); // 允许最多3个线程同时进入临界区
void access_resource(int id) {
sem.wait(); // 等待进入临界区
std::cout << "Thread " << id << " is accessing the resource.\n";
// 模拟资源访问
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Thread " << id << " is leaving the resource.\n";
sem.notify(); // 离开临界区,通知其他线程
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.emplace_back(access_resource, i);
}
for (auto& t : threads) {
t.join();
}
return 0;
}
在这个示例中,我们创建了一个初始计数为 3 的 std::semaphore
,表示最多允许 3 个线程同时访问资源。然后,我们创建了 5 个线程,每个线程都尝试访问资源。由于信号量的限制,只有 3 个线程可以同时进入临界区,其他线程必须等待直到有线程离开并释放信号量。