网站怎么做登录界面,企业网页制作心得,志鸿优化设计答案,苏州工作平台有哪些公司C线程同步之条件变量 文章目录 C线程同步之条件变量什么是条件变量#xff08;Condition Variable#xff09;#xff1f;条件变量的主要用途常见的应用场景C11中的条件变量condition_variable的使用方法std::condition_variable的使用步骤典型的使用示例#xff1a;生产者…C线程同步之条件变量 文章目录 C线程同步之条件变量什么是条件变量Condition Variable条件变量的主要用途常见的应用场景C11中的条件变量condition_variable的使用方法std::condition_variable的使用步骤典型的使用示例生产者-消费者模型 condition_variable_any的使用方法std::condition_variable_any的使用步骤典型的使用示例生产者-消费者模型 condition_variable_any vs std::condition_variable注意事项小结 什么是条件变量Condition Variable
条件变量condition_variable是一种线程同步机制用于在多线程程序中协调线程之间的执行顺序。它通过允许线程在某些条件成立时被唤醒或者当某些条件不满足时进入等待状态从而实现线程的协调和同步。与互斥锁mutex配合使用条件变量允许线程在某个条件发生变化时继续执行。
条件变量的主要用途
条件变量的核心作用是
线程等待某个条件当一个线程需要等待某个特定条件发生时它可以通过条件变量进入等待状态。比如等待队列中有元素时才能进行消费操作。通知其他线程当条件发生变化时其他线程可以通知正在等待的线程继续执行。比如生产者线程生产了新的数据通知消费者线程可以开始处理数据。
在并发编程中条件变量通常与互斥锁一起使用来确保数据访问的一致性。
常见的应用场景
生产者-消费者问题多个生产者线程和多个消费者线程共享一个缓冲区生产者将数据放入缓冲区消费者从缓冲区取数据。在缓冲区为空时消费者线程等待在缓冲区已满时生产者线程等待。条件变量可以用来协调生产者和消费者的执行顺序。线程池当线程池中的线程正在处理任务时如果没有任务线程会进入等待状态直到任务被提交线程才会被唤醒并执行任务。线程之间的事件通知比如一个线程执行一些初始化操作另一个线程依赖于该操作完成后才能继续执行。通过条件变量一个线程可以在另一个线程完成初始化后收到通知。共享资源的访问多个线程需要访问一个共享资源在某些情况下资源可能不可用比如池中没有空闲资源。线程可以等待资源可用并在条件满足时继续执行。
C11中的条件变量
在C11中标准库引入了条件变量类提供了两种主要的条件变量
std::condition_variable这是最常用的条件变量适用于需要在多个线程间协调的场景。它基于std::mutex或std::unique_lockstd::mutex来进行线程同步。std::condition_variable_any这是一个通用的条件变量它可以与任意类型的锁一起使用不仅限于std::mutex但它的使用场景相对较少。
condition_variable的使用方法
std::condition_variable的使用步骤
创建一个条件变量。使用std::mutex或std::unique_lock来保护共享数据。使用wait函数使线程进入等待状态直到接收到通知notify_one或notify_all。使用notify_one或notify_all通知一个或所有等待的线程。
典型的使用示例生产者-消费者模型
#include cstddef
#include cstdio
#include cstdlib
#include iostream
#include queue
#include thread
#include condition_variable
#include mutexusing namespace std;std::queueint buffer;
std::mutex mtx;
condition_variable m_QueueEmpty; // 队列为空的条件变量
condition_variable m_QueueFull; // 队列已满的条件变量const size_t max_buffer_size 5;void producer() {std::unique_lockstd::mutex lock(mtx);// 队列已满while (buffer.size() max_buffer_size) {cout buffer is full, producer wait... endl;m_QueueFull.wait(lock);}// 生产数据 模拟生产数据 随机获取1~100的随机数int data rand() % 100 1;buffer.push(data);auto threadid this_thread::get_id();printf(produce data: %d , thread id: %lld \n,data, threadid);// 通知消费者可以消费数据m_QueueEmpty.notify_one();
}void consumer() {unique_lockmutex lock(mtx);while (buffer.empty()) {cout buffer is empty, consumer wait... endl;m_QueueEmpty.wait(lock);}int data buffer.front();buffer.pop();auto threadid this_thread::get_id();printf(consume data: %d , thread id: %lld \n,data, threadid);// 通知生产者可以生产数据m_QueueFull.notify_one();
}int main() {thread t1[15];thread t2[15];for (int i 0; i 15; i){t1[i] thread(producer);t2[i] thread(consumer);}for (int i 0; i 15; i){t1[i].join();t2[i].join();}return 0;
}条件变量condition_variable类的wait()还有一个重载的方法可以接受一个条件这个条件也可以是一个返回值为布尔类型的函数条件变量会先检查判断这个条件是否满足如果满足条件布尔值为true则当前线程重新获得互斥锁的所有权结束阻塞继续向下执行如果不满足条件布尔值为false当前线程会释放互斥锁解锁同时被阻塞等待被唤醒。
这样代码就可以优化一下:
void producer() {std::unique_lockstd::mutex lock(mtx);// 队列已满m_QueueFull.wait(lock, []{ return buffer.size() max_buffer_size; });// 生产数据 模拟生产数据 随机获取1~100的随机数int data rand() % 100 1;buffer.push(data);auto threadid this_thread::get_id();printf(produce data: %d , thread id: %lld \n,data, threadid);// 通知消费者可以消费数据m_QueueEmpty.notify_one();
}void consumer() {unique_lockmutex lock(mtx);m_QueueEmpty.wait(lock, []{ return !buffer.empty(); });int data buffer.front();buffer.pop();auto threadid this_thread::get_id();printf(consume data: %d , thread id: %lld \n,data, threadid);// 通知生产者可以生产数据m_QueueFull.notify_one();
}修改之后可以发现程序变得更加精简了而且执行效率更高了因为在这两个函数中的while循环被删掉了但是最终的效果是一样的推荐使用这种方式的wait()进行线程的阻塞。
condition_variable_any的使用方法
condition_variable_any 是 C 标准库中用于同步的一个条件变量它允许线程在某些条件满足时继续执行。与标准的 std::condition_variable 相比condition_variable_any 在底层实现上更加灵活能够支持更多类型的锁比如 std::mutex 和 std::shared_mutex而 std::condition_variable 只能与 std::unique_lockstd::mutex 配合使用。
std::condition_variable_any的使用步骤
std::condition_variable_any 的基本使用方式与 std::condition_variable 类似
创建一个 condition_variable_any 对象。在某些条件下等待wait当条件满足时通知其他线程notify_one 或 notify_all。使用合适的锁来保护共享数据。
典型的使用示例生产者-消费者模型
#include iostream
#include thread
#include condition_variable
#include mutex
#include queuestd::mutex mtx; // 互斥锁
std::condition_variable_any cv; // 条件变量
std::queueint buffer; // 模拟缓冲区
const int BUFFER_SIZE 5; // 缓冲区最大容量// 生产者线程
void producer() {int product 0;while (true) {std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 模拟生产过程{std::lock_guardstd::mutex lock(mtx);if (buffer.size() BUFFER_SIZE) {buffer.push(product);std::cout Produced: product std::endl;cv.notify_one(); // 通知消费者有新的产品} else {std::cout Buffer is full. Producer is waiting. std::endl;cv.wait(mtx, []{ return buffer.size() BUFFER_SIZE; }); // 等待缓冲区有空间}}}
}// 消费者线程
void consumer() {while (true) {std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 模拟消费过程{std::lock_guardstd::mutex lock(mtx);if (buffer.empty()) {std::cout Buffer is empty. Consumer is waiting. std::endl;cv.wait(mtx, []{ return !buffer.empty(); }); // 等待缓冲区有数据} else {int product buffer.front();buffer.pop();std::cout Consumed: product std::endl;cv.notify_one(); // 通知生产者可以继续生产}}}
}int main() {std::thread producerThread(producer);std::thread consumerThread(consumer);producerThread.join();consumerThread.join();return 0;
}
生产者线程生产者线程首先会检查缓冲区是否有空间如果缓冲区已满生产者将等待直到缓冲区有空间。使用 cv.wait(mtx, predicate) 来进行等待其中 predicate 是一个 lambda 表达式它会在缓冲区有空间时返回 true通知线程继续执行。消费者线程消费者线程首先会检查缓冲区是否为空。如果为空消费者将等待直到缓冲区有数据。使用同样的 cv.wait(mtx, predicate) 来进行等待。互斥锁 (mtx)我们使用 std::mutex 来保证对缓冲区的互斥访问确保线程安全。条件变量的通知当生产者生产了一个新产品或消费者消费了一个产品时会通过 cv.notify_one() 来通知另一个线程继续执行。
condition_variable_any vs std::condition_variable
std::condition_variable_any 的主要优势在于它不局限于与 std::mutex 一起使用还可以与其他类型的锁配合使用比如 std::shared_mutex。在大多数情况下std::condition_variable_any 和 std::condition_variable 用法类似。如果你不需要特别灵活的锁类型std::condition_variable 更常见。
注意事项
避免虚假唤醒cv.wait() 会在被唤醒后重新检查条件因此要使用 cv.wait(mtx, predicate) 这种带条件的等待方式而不是简单地调用 cv.wait(mtx)以防止虚假唤醒。使用 notify_one() 或 notify_all()通常我们使用 notify_one() 来唤醒一个等待线程或者使用 notify_all() 来唤醒所有等待线程。在生产者-消费者模型中notify_one() 是常用的选择因为每次只有一个线程被唤醒。
小结
condition_variable_any 提供了一个灵活的方式来实现线程间的同步尤其是对于更复杂的锁类型如 std::shared_mutex。在生产者-消费者模型中它有助于实现线程间的协调使得生产者在缓冲区满时等待消费者在缓冲区空时等待通过适时的通知机制有效地协调生产和消费过程。