1.介绍
condition_variable
是一个条件变量,它用于实现线程间的同步。它通常与互斥锁一起使用,以便在等待某个条件时释放锁,并在条件满足时重新获取锁。
可以把 condition_variable
想象成一个餐厅的服务铃。当顾客需要服务时,他会按响服务铃,服务员就会过来为他服务。如果服务员正在忙,顾客就必须等待。当服务员完成工作后,他会检查服务铃是否响过,如果响过,他就会去为顾客服务。
2.例子
下面是一个简单的代码例子,演示了如何使用 condition_variable
来实现线程间的同步:
#include <iostream> #include <thread> #include <mutex> #include <condition_variable> std::mutex m; std::condition_variable cv; bool ready = false; void print_id(int id) { std::unique_lock<std::mutex> lk(m); while (!ready) { cv.wait(lk); } std::cout << "thread " << id << '\n'; } void go() { std::unique_lock<std::mutex> lk(m); ready = true; cv.notify_all(); } int main() { std::thread threads[10]; for (int i = 0; i < 10; ++i) threads[i] = std::thread(print_id, i); std::cout << "10 threads ready to race...\n"; go(); for (auto& th : threads) th.join(); return 0; }
在这个例子中,有10个线程等待一个条件变量。当主线程调用 go
函数时,它会通知所有等待的线程继续执行
这段代码定义了一个互斥锁 m
,一个条件变量 cv
和一个布尔变量 ready
。print_id
函数接受一个整数参数 id
,表示线程的编号。在函数内部,首先创建了一个 unique_lock
对象 lk
,用于管理互斥锁。然后,使用 while
循环检查 ready
变量的值。如果 ready
为 false
,则调用条件变量的 wait
函数等待条件满足。当条件满足时(即 ready
变为 true
),循环结束,输出线程编号。
go
函数用于通知所有等待的线程继续执行。在函数内部,首先创建了一个 unique_lock
对象 lk
,用于管理互斥锁。然后,将 ready
变量的值设为 true
,表示条件已经满足。最后,调用条件变量的 notify_all
函数通知所有等待的线程。
在 main
函数中,首先创建了一个名为 threads
的数组,用于存储线程对象。然后,使用循环创建了10个线程,每个线程都调用 print_id
函数,并传入一个整数参数作为线程编号。
接下来,输出一行提示信息,表示所有线程都已经准备好。然后,调用 go
函数通知所有等待的线程继续执行。
最后,使用一个循环等待所有线程执行完毕。
在这个例子中,所有线程都在等待一个条件变量。当主线程调用 go
函数时,它会通知所有等待的线程继续执行
这里可能有人会问:notify_all唤醒等待的线程后,是所有线程同时执行吗,还是说一个个执行?
当调用条件变量的 notify_all
函数唤醒所有等待的线程时,这些线程会从等待状态变为就绪状态。然后,操作系统调度器会根据其调度策略选择就绪状态的线程并将其调度到可用的处理器上运行。
这意味着,被唤醒的线程并不是同时开始执行,而是由操作系统调度器决定它们的执行顺序。在多处理器系统中,被唤醒的线程可能会并行执行;而在单处理器系统中,被唤醒的线程只能串行执行。
因此,被唤醒的线程的执行顺序和并行性取决于操作系统调度器和硬件环境
那会不会并行执行就不安全了?
并行执行线程本身并不会导致线程不安全。线程安全问题通常发生在多个线程访问共享数据时,如果对共享数据的访问没有进行适当的同步,就可能出现数据竞争和其他线程安全问题。
在上面给出的 condition_variable
的例子中,所有线程都在等待一个条件变量。当主线程调用 go
函数时,它会通知所有等待的线程继续执行。由于每个线程都只访问自己的局部变量,而不访问共享数据,所以这个例子中的线程是安全的,即使它们并行执行也不会出现问题。
如果多个线程需要访问共享数据,那么就需要使用同步机制(如互斥锁、原子变量等)来保证线程安全。这样,即使多个线程并行执行,也不会出现数据竞争和其他线程安全问题
每个线程都只访问自己的局部变量,这里的局部变量是什么?
局部变量是指在函数内部定义的变量。它们只能在定义它们的函数内部访问,当函数执行完毕后,它们就会被销毁。
在上面给出的 condition_variable
的例子中,print_id
函数中定义了一个名为 lk
的局部变量,它是一个 unique_lock
对象,用于管理互斥锁。这个变量只能在 print_id
函数内部访问,当函数执行完毕后,它就会被销毁。
由于每个线程都调用了一次 print_id
函数,所以每个线程都有自己的 lk
变量。这些变量彼此独立,互不影响。因此,即使多个线程并行执行,也不会出现数据竞争和其他线程安全问题