您好,登录后才能下订单哦!
在多线程编程中,线程之间的同步是一个非常重要的问题。C++11引入了std::condition_variable
,它是一种用于线程间同步的工具,允许一个或多个线程等待某个条件成立,或者通知其他线程条件已经成立。本文将详细介绍std::condition_variable
的使用方法、典型应用场景以及注意事项。
std::condition_variable
是C++11标准库中提供的一种同步原语,用于在多线程环境中实现线程间的条件等待和通知。它通常与std::mutex
一起使用,以确保线程在等待条件时能够正确地释放锁,并在条件满足时重新获取锁。
条件变量的核心思想是:一个线程可以等待某个条件成立,而另一个线程可以在条件成立时通知等待的线程。这种机制非常适合用于实现生产者-消费者模型、线程池等并发模式。
要使用std::condition_variable
,首先需要包含头文件<condition_variable>
,然后创建一个std::condition_variable
对象:
#include <condition_variable>
std::condition_variable cv;
线程可以使用std::condition_variable::wait
函数来等待某个条件成立。wait
函数需要一个std::unique_lock<std::mutex>
对象作为参数,并且在等待时会自动释放锁,直到条件满足时重新获取锁。
std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return condition; });
在上面的代码中,cv.wait
会一直等待,直到condition
为true
。wait
函数会自动释放lock
,并在条件满足时重新获取锁。
当一个线程满足某个条件时,可以使用std::condition_variable::notify_one
或std::condition_variable::notify_all
来通知等待的线程。
notify_one
:唤醒一个等待的线程。notify_all
:唤醒所有等待的线程。{
std::lock_guard<std::mutex> lock(mtx);
condition = true;
}
cv.notify_one(); // 或者 cv.notify_all();
在上面的代码中,notify_one
会唤醒一个等待的线程,而notify_all
会唤醒所有等待的线程。
生产者-消费者模型是多线程编程中的一个经典问题。生产者线程生成数据并将其放入缓冲区,而消费者线程从缓冲区中取出数据并进行处理。条件变量可以很好地解决生产者和消费者之间的同步问题。
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
std::queue<int> buffer;
std::mutex mtx;
std::condition_variable cv;
const int max_buffer_size = 10;
void producer() {
for (int i = 0; i < 20; ++i) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return buffer.size() < max_buffer_size; });
buffer.push(i);
std::cout << "Produced: " << i << std::endl;
lock.unlock();
cv.notify_all();
}
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !buffer.empty(); });
int value = buffer.front();
buffer.pop();
std::cout << "Consumed: " << value << std::endl;
lock.unlock();
cv.notify_all();
}
}
int main() {
std::thread prod(producer);
std::thread cons(consumer);
prod.join();
cons.join();
return 0;
}
在上面的代码中,生产者线程在缓冲区未满时生成数据并放入缓冲区,消费者线程在缓冲区不为空时从缓冲区中取出数据。条件变量cv
用于同步生产者和消费者线程。
线程池是一种常见的并发模式,它通过预先创建一组线程来处理任务。条件变量可以用于线程池中的任务调度。
#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
class ThreadPool {
public:
ThreadPool(size_t num_threads) {
for (size_t i = 0; i < num_threads; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(this->queue_mutex);
this->condition.wait(lock, [this]{ return this->stop || !this->tasks.empty(); });
if (this->stop && this->tasks.empty()) {
return;
}
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
});
}
}
template<class F, class... Args>
void enqueue(F&& f, Args&&... args) {
{
std::unique_lock<std::mutex> lock(queue_mutex);
tasks.emplace([=]{ return f(args...); });
}
condition.notify_one();
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for (std::thread &worker : workers) {
worker.join();
}
}
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queue_mutex;
std::condition_variable condition;
bool stop = false;
};
int main() {
ThreadPool pool(4);
for (int i = 0; i < 8; ++i) {
pool.enqueue([i] {
std::cout << "Task " << i << " is running" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Task " << i << " is done" << std::endl;
});
}
return 0;
}
在上面的代码中,线程池中的线程会等待任务队列中的任务,并在任务到来时执行任务。条件变量condition
用于通知线程有新任务到来。
虚假唤醒是指线程在没有收到通知的情况下从wait
函数中返回。为了防止虚假唤醒,通常需要在wait
函数中使用一个谓词来检查条件是否真正满足。
cv.wait(lock, []{ return condition; });
在上面的代码中,wait
函数会在条件满足时返回,从而避免虚假唤醒。
在使用条件变量时,如果锁的使用不当,可能会导致死锁。例如,如果一个线程在持有锁的情况下调用wait
,而另一个线程在持有锁的情况下调用notify
,可能会导致死锁。
为了避免死锁,通常需要在调用wait
之前释放锁,并在wait
返回时重新获取锁。
条件变量的性能通常比忙等待(busy-waiting)要好,因为它可以让线程在等待时进入睡眠状态,从而减少CPU的占用。然而,条件变量的性能也受到锁的竞争和上下文切换的影响,因此在设计多线程程序时需要仔细考虑锁的粒度和条件变量的使用。
std::condition_variable
提供了wait_for
和wait_until
函数,允许线程在等待条件时设置超时时间。
std::cv_status status = cv.wait_for(lock, std::chrono::seconds(1));
if (status == std::cv_status::timeout) {
// 超时处理
}
在上面的代码中,wait_for
函数会在1秒后返回,如果条件仍未满足,则返回std::cv_status::timeout
。
条件变量通常与std::mutex
或std::unique_lock
结合使用,以确保线程在等待条件时能够正确地释放锁,并在条件满足时重新获取锁。
std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return condition; });
在上面的代码中,wait
函数会自动释放lock
,并在条件满足时重新获取锁。
std::condition_variable
是C++11中用于线程间同步的重要工具,它允许线程等待某个条件成立,并在条件满足时通知其他线程。通过合理地使用条件变量,可以实现复杂的多线程同步机制,如生产者-消费者模型、线程池等。然而,在使用条件变量时也需要注意虚假唤醒、死锁等问题,以确保程序的正确性和性能。
希望本文能够帮助你更好地理解和使用C++11中的std::condition_variable
。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。