condition_variable

简介: condition_variable

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 和一个布尔变量 readyprint_id 函数接受一个整数参数 id,表示线程的编号。在函数内部,首先创建了一个 unique_lock 对象 lk,用于管理互斥锁。然后,使用 while 循环检查 ready 变量的值。如果 readyfalse,则调用条件变量的 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 变量。这些变量彼此独立,互不影响。因此,即使多个线程并行执行,也不会出现数据竞争和其他线程安全问题

相关文章
|
网络安全 数据安全/隐私保护
ssh远程执行命令自动输入密码方式
ssh远程执行命令自动输入密码方式
3309 0
|
10月前
|
人工智能 JavaScript 测试技术
如何在云效中使用DeepSeek等大模型实现AI智能评审
本文介绍了一种基于云效平台和DeepSeek的AI智能代码评审解决方案。通过云效代码管理Codeup、流水线Flow和DeepSeek大模型,企业可快速实现自动化代码评审。具体步骤包括:创建云效组织并获取API令牌、配置百炼APIKey、开发自定义Flow步骤调用大模型、创建示例代码库和流水线,并通过合并请求触发智能评审。最终,评审结果将自动回写到Codeup中,帮助开发者提升代码质量。
1863 11
如何在云效中使用DeepSeek等大模型实现AI智能评审
|
人工智能 Python
蓝桥杯练习题(四):Python组之历届试题三十题
关于蓝桥杯Python组历届试题的三十个练习题的总结,包括题目描述、输入输出格式、样例输入输出以及部分题目的解题思路和代码实现。
502 0
蓝桥杯练习题(四):Python组之历届试题三十题
|
JSON Kubernetes 数据格式
crictl 常见的命令大全
crictl(Container Runtime Interface Command Line Interface)是一个命令行工具,用于与符合Kubernetes容器运行时接口(CRI)规范的容器运行时进行交互。它提供了一系列命令来查看和管理容器、镜像、Pod等资源。以下是crictl的一些常见命令及其功能概述: ### 1. 镜像管理 * **查看镜像**: - `crictl images`:列出所有镜像。 - `crictl images | grep <image-name>`:查看特定镜像。 * **拉取镜像**: - `crictl pull <image_na
5564 8
|
前端开发
招投标系统是Electron的纯内网编辑Office Word,可以设置部分区域可编辑,其他的地方不能编辑吗?
我们是招投标系统的开发公司,框架是用的Electron,需要在纯内网的环境下编辑Office Word,可以设置部分区域可编辑,其他的地方不能编辑吗(如下红框位置)?并且在用户忘记填写一些区域的时候做提醒。
291 85
|
存储 Linux API
C++新特性 线程局部存储
C++新特性 线程局部存储
384 0
|
存储 编解码 API
【图像文本化】Base64编解码OpenCV4中 Mat 对象
【图像文本化】Base64编解码OpenCV4中 Mat 对象
349 0
|
存储 监控 安全
深度剖析Linux进程的内部机制:一探/proc/pid的奥秘
深度剖析Linux进程的内部机制:一探/proc/pid的奥秘
2745 0
|
存储 设计模式 算法
[C++] static静态成员变量/函数的用法
[C++] static静态成员变量/函数的用法
429 1