std::thread的同步机制

在 C++ 中,std::thread 用于创建和管理线程。为了确保多个线程能正确、安全地访问共享资源,避免数据竞争和不一致问题,需要使用同步机制。

互斥锁(std::mutex)

原理:互斥锁是一种最基本的同步原语,用于保护共享资源。同一时间只允许一个线程访问被互斥锁保护的代码段,其他线程必须等待该线程释放锁后才能继续访问。

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int shared_variable = 0;

void increment() {
    for (int i = 0; i < 100000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);
        ++shared_variable;
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "Shared variable: " << shared_variable << std::endl;
    return 0;
}

std::lock_guard 是一个 RAII(资源获取即初始化)类,它在构造时自动锁定互斥锁 mtx,在析构时自动解锁。这样可以确保对 shared_variable 的访问是线程安全的。
increment()函数运行结束,锁就会被释放。

递归互斥锁(std::recursive_mutex)

原理:递归互斥锁允许同一线程多次锁定该互斥锁,而不会导致死锁。当线程第一次锁定递归互斥锁时,它会记录锁定的次数,每次解锁时,锁定次数减 1,直到锁定次数为 0 时,互斥锁才真正被释放。

#include <iostream>
#include <thread>
#include <mutex>

std::recursive_mutex rmtx;

void recursive_function(int n) {
    std::lock_guard<std::recursive_mutex> lock(rmtx);
    if (n > 0) {
        std::cout << "Recursive call: " << n << std::endl;
        recursive_function(n - 1);
    }
}

int main() {
    std::thread t(recursive_function, 5);
    t.join();
    return 0;
}

在 recursive_function 中,同一线程可以多次锁定 rmtx,而不会导致死锁。
创建了一个新线程,该线程会执行 recursive_function 函数,并将 5 作为参数传递给它

定时互斥锁(std::timed_mutex 和 std::recursive_timed_mutex)

原理:定时互斥锁允许线程在尝试锁定互斥锁时设置一个超时时间。如果在超时时间内未能锁定互斥锁,线程可以继续执行其他任务,而不会一直等待。

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

std::timed_mutex tmtx;

void try_lock_with_timeout() {
    if (tmtx.try_lock_for(std::chrono::milliseconds(500))) {
        std::cout << "Locked the mutex." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
        tmtx.unlock();
    } else {
        std::cout << "Failed to lock the mutex within the timeout." << std::endl;
    }
}

int main() {
    std::thread t(try_lock_with_timeout);
    t.join();
    return 0;
}

在 try_lock_with_timeout 函数中,线程尝试在 500 毫秒内锁定 tmtx。如果成功锁定,则执行相应的操作并解锁;如果超时未能锁定,则输出失败信息。

条件变量(std::condition_variable)

原理:条件变量用于线程间的等待 - 通知机制。一个线程可以等待某个条件成立,而另一个线程可以在条件成立时通知等待的线程。条件变量通常与互斥锁一起使用,以确保线程安全。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

std::queue<int> dataQueue;
std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void consumer() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return ready; });
    while (!dataQueue.empty()) {
        std::cout << "Consumed: " << dataQueue.front() << std::endl;
        dataQueue.pop();
    }
}

void producer() {
    for (int i = 0; i < 5; ++i) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        {
            std::unique_lock<std::mutex> lock(mtx);
            dataQueue.push(i);
            std::cout << "Produced: " << i << std::endl;
        }
    }
    {
        std::unique_lock<std::mutex> lock(mtx);
        ready = true;
    }
    cv.notify_one();
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);

    t1.join();
    t2.join();

    return 0;
}

consumer 线程等待 ready 条件成立,producer 线程在生产完所有数据后将 ready 设置为 true,并通知 consumer 线程。cv.wait 会自动释放互斥锁,直到条件成立时再重新锁定互斥锁。

原子操作(std::atomic)

原理:原子操作是一种无锁的同步机制,用于对基本数据类型进行原子读写操作。原子操作保证了操作的不可分割性,避免了数据竞争。

#include <iostream>
#include <thread>
#include <atomic>

std::atomic<int> atomic_variable(0);

void increment_atomic() {
    for (int i = 0; i < 100000; ++i) {
        ++atomic_variable;
    }
}

int main() {
    std::thread t1(increment_atomic);
    std::thread t2(increment_atomic);

    t1.join();
    t2.join();

    std::cout << "Atomic variable: " << atomic_variable << std::endl;
    return 0;
}

std::atomic 定义了一个原子整数变量 atomic_variable。对 atomic_variable 的自增操作是原子的,不需要使用互斥锁来保护。

std::thread的future

在 C++ 中,std::thread 用于创建和管理线程,而 std::future 是 C++ 标准库中用于异步操作的一个重要组件,它可以与 std::thread 结合使用来获取异步任务的结果。

std::future 概述

std::future 是一个模板类,定义在 头文件中。它提供了一种机制,允许一个线程等待另一个线程的异步操作结果。当一个异步操作启动时,会返回一个 std::future 对象,通过该对象可以在需要的时候获取异步操作的返回值。

std::thread 结合使用的基本步骤

通常情况下,不直接将 std::future 与 std::thread 结合,而是使用 std::async 来创建异步任务并返回 std::future 对象。不过,也可以手动模拟类似的机制。

手动模拟(结合 std::promise)

std::promise 是一个用于存储值或异常的对象,它可以与 std::future 关联起来,std::promise 设置的值可以通过与之关联的 std::future 获取

#include <iostream>
#include <thread>
#include <future>

// 线程函数
void task(std::promise<int>& prom) {
    // 模拟一些耗时操作
    std::this_thread::sleep_for(std::chrono::seconds(2));
    int result = 42;
    // 设置 promise 的值
    prom.set_value(result);
}

int main() {
    // 创建一个 promise 对象
    std::promise<int> prom;
    // 获取与 promise 关联的 future 对象
    std::future<int> fut = prom.get_future();

    // 创建线程并传入 promise 对象
    std::thread t(task, std::ref(prom));

    // 等待异步任务完成并获取结果
    int value = fut.get();
    std::cout << "The result is: " << value << std::endl;

    // 等待线程结束
    t.join();

    return 0;
}

std::promise prom;:创建一个 std::promise 对象,用于存储一个 int 类型的值。
std::future fut = prom.get_future();:通过 promise 的 get_future 方法获取与之关联的 std::future 对象,用于获取异步操作的结果。
std::thread t(task, std::ref(prom));:创建一个新线程,将 task 函数作为线程函数,并将 promise 对象的引用传递给它。
int value = fut.get();:调用 future 的 get 方法,该方法会阻塞当前线程,直到异步任务完成并设置了 promise 的值,然后返回该值。
t.join();:等待线程结束,确保资源正确释放。

使用 std::async

std::async 是一个更方便的异步操作启动函数,它会自动管理线程和 std::future 对象。

#include <iostream>
#include <future>

// 异步任务函数
int asyncTask() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}

int main() {
    // 启动异步任务并获取 future 对象
    std::future<int> fut = std::async(std::launch::async, asyncTask);

    // 等待异步任务完成并获取结果
    int value = fut.get();
    std::cout << "The result is: " << value << std::endl;

    return 0;
}

std::future fut = std::async(std::launch::async, asyncTask);:调用 std::async 函数启动一个异步任务,std::launch::async 表示立即启动一个新线程来执行任务,asyncTask 是要执行的任务函数。std::async 会返回一个 std::future 对象,用于获取任务的结果。
int value = fut.get();:调用 future 的 get 方法,阻塞当前线程直到任务完成并返回结果。

std::future 的主要方法

get():用于获取异步操作的结果。如果异步操作尚未完成,调用该方法会阻塞当前线程,直到结果可用。
wait():等待异步操作完成,但不获取结果。该方法会阻塞当前线程,直到异步操作结束。
wait_for():等待异步操作在指定的时间内完成。如果在指定时间内操作完成,返回 std::future_status::ready;如果超时,返回 std::future_status::timeout;如果操作尚未开始,返回 std::future_status::deferred。
wait_until():等待异步操作直到指定的时间点。返回值与 wait_for 类似。

get() 方法示例

get() 方法用于获取异步操作的结果,若操作未完成,调用线程会被阻塞,直至结果可用。

#include <iostream>
#include <future>
#include <thread>

// 模拟一个耗时的异步任务
int asyncTask() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 42;
}

int main() {
    // 使用 std::async 启动异步任务并获取 std::future 对象
    std::future<int> fut = std::async(std::launch::async, asyncTask);

    std::cout << "Waiting for the result..." << std::endl;
    // 调用 get() 方法获取结果,若任务未完成会阻塞
    int result = fut.get();
    std::cout << "The result of the async task is: " << result << std::endl;

    return 0;
}

std::async(std::launch::async, asyncTask) 启动一个新线程执行 asyncTask 函数,并返回一个 std::future 对象 fut。
fut.get() 调用会阻塞主线程,直到 asyncTask 完成并返回结果,然后将结果赋值给 result 变量。

wait() 方法示例

wait() 方法用于等待异步操作完成,但不获取结果,调用线程会被阻塞直到操作结束。

#include <iostream>
#include <future>
#include <thread>

// 模拟一个耗时的异步任务
void asyncTask() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "Async task is done." << std::endl;
}

int main() {
    // 使用 std::async 启动异步任务并获取 std::future 对象
    std::future<void> fut = std::async(std::launch::async, asyncTask);

    std::cout << "Waiting for the async task to complete..." << std::endl;
    // 调用 wait() 方法等待任务完成
    fut.wait();
    std::cout << "The async task has completed." << std::endl;

    return 0;
}

std::async(std::launch::async, asyncTask) 启动一个新线程执行 asyncTask 函数,并返回一个 std::future 对象 fut。
fut.wait() 调用会阻塞主线程,直到 asyncTask 完成。

wait_for() 方法示例

wait_for() 方法用于在指定的时间内等待异步操作完成,并根据结果返回不同的 std::future_status。

#include <iostream>
#include <future>
#include <thread>
#include <chrono>

// 模拟一个耗时的异步任务
int asyncTask() {
    std::this_thread::sleep_for(std::chrono::seconds(3));
    return 42;
}

int main() {
    // 使用 std::async 启动异步任务并获取 std::future 对象
    std::future<int> fut = std::async(std::launch::async, asyncTask);

    std::cout << "Waiting for the async task with a timeout..." << std::endl;
    // 等待 2 秒
    auto status = fut.wait_for(std::chrono::seconds(2));

    if (status == std::future_status::ready) {
        std::cout << "The async task is ready. Result: " << fut.get() << std::endl;
    } else if (status == std::future_status::timeout) {
        std::cout << "Timed out while waiting for the async task." << std::endl;
    } else if (status == std::future_status::deferred) {
        std::cout << "The async task is deferred." << std::endl;
    }

    return 0;
}

std::async(std::launch::async, asyncTask) 启动一个新线程执行 asyncTask 函数,并返回一个 std::future 对象 fut。
fut.wait_for(std::chrono::seconds(2)) 会等待 2 秒,根据等待结果返回 std::future_status 枚举值。
根据 status 的不同值进行不同的处理。

wait_until() 方法示例

wait_until() 方法用于等待异步操作直到指定的时间点,并根据结果返回不同的 std::future_status。

#include <iostream>
#include <future>
#include <thread>
#include <chrono>

// 模拟一个耗时的异步任务
int asyncTask() {
    std::this_thread::sleep_for(std::chrono::seconds(3));
    return 42;
}

int main() {
    // 使用 std::async 启动异步任务并获取 std::future 对象
    std::future<int> fut = std::async(std::launch::async, asyncTask);

    // 计算 2 秒后的时间点
    auto timeout_time = std::chrono::steady_clock::now() + std::chrono::seconds(2);
    std::cout << "Waiting for the async task until a specific time..." << std::endl;
    // 等待直到指定时间点
    auto status = fut.wait_until(timeout_time);

    if (status == std::future_status::ready) {
        std::cout << "The async task is ready. Result: " << fut.get() << std::endl;
    } else if (status == std::future_status::timeout) {
        std::cout << "Timed out while waiting for the async task." << std::endl;
    } else if (status == std::future_status::deferred) {
        std::cout << "The async task is deferred." << std::endl;
    }

    return 0;
}

std::async(std::launch::async, asyncTask) 启动一个新线程执行 asyncTask 函数,并返回一个 std::future 对象 fut。
std::chrono::steady_clock::now() + std::chrono::seconds(2) 计算当前时间 2 秒后的时间点 timeout_time。
fut.wait_until(timeout_time) 会等待直到 timeout_time,根据等待结果返回 std::future_status 枚举值。
根据 status 的不同值进行不同的处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/975918.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot+Mybatis-Plus实现动态数据源

目录 一、前言二、代码实现1&#xff09;工程结构2&#xff09;相关依赖3&#xff09;数据源拦截切面4&#xff09;动态数据源切换5&#xff09;核心配置类6&#xff09;使用 三、原理分析1&#xff09;mapper接口注入流程2&#xff09;动态数据源切换执行流程 四、声明式事务导…

进程概念、PCB及进程查看

文章目录 一.进程的概念进程控制块&#xff08;PCB&#xff09; 二.进程查看通过指令查看进程通过proc目录查看进程的cwd和exe获取进程pid和ppid通过fork()创建子进程 一.进程的概念 进程是一个运行起来的程序&#xff0c;而程序是存放在磁盘的&#xff0c;cpu要想执行程序的指…

字节火山引擎 DeepSeek 接入本地使用

文章目录 1. 火山引擎 DeepSeek 初体验2. 本地接入 火山引擎 DeepSeek API3. 新建 API KEY4. 直接使用 1. 火山引擎 DeepSeek 初体验 火山引擎官网 : https://www.volcengine.com/product/ark 火山云默认给每个模型赠送 50 万 tokens 推理免费额度 进来就会看到模型广场&#…

基于javaweb的SpringBoot个人博客系统设计和实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

《操作系统 - 清华大学》8 -4:进程管理:进程控制结构

深度剖析进程控制块&#xff1a;操作系统进程管理的核心关键 在操作系统的复杂体系中&#xff0c;进程控制块&#xff08;PCB&#xff09;是实现高效进程管理的关键所在。接下来&#xff0c;将从多个维度深入剖析进程控制块&#xff0c;帮助更好地理解其在操作系统中的重要作用…

Jupyter里面的manim编程学习

1.Jupyterlab的使用 因为我之前一直都是使用的vscode进行manim编程的&#xff0c;但是今天看的这个教程使用的是Jupyter&#xff0c;我也很是好奇这个manim在Jupyter这样的交互式下面会生成怎么样的效果&#xff0c;所以今天尝试了jupyter&#xff0c;并且对于两个进行比较和说…

孜然单授权系统V2.0PHP授权系统

孜然单授权V1.0系统&#xff0c;延续了2022年开发的孜然多应用授权系统V2.0 变更&#xff1a;多应用变单系统&#xff0c;去除没用的垃圾代码&#xff0c;从0开发&#xff0c;去除了一些没用的功能 完善了开发文档&#xff0c;之前那套是我写着玩的屎山代码&#xff0c;V1.0将展…

输入菜单关键字,遍历匹配到 menuIds,展开 匹配节点 的所有父节点以及 匹配节点 本身,高亮 匹配节点

菜单检索&#xff0c;名称、地址、权限标志 等 关键字匹配、展开、高亮(全程借助 DeepSeek ) 便捷简洁的企业官网 的后台菜单管理&#xff0c;图示&#xff1a; 改造点&#xff1a; &#xff08;1&#xff09;修改 bootstrapTreeTable 的节点class命名方式为&#xff1a;treeg…

【落羽的落羽 数据结构篇】顺序结构的二叉树——堆

文章目录 一、堆1. 概念与分类2. 结构与性质3. 入堆4. 出堆 二、堆排序三、堆排序的应用——TOP-K问题 一、堆 1. 概念与分类 上一期我们提到&#xff0c;二叉树的实现既可以用顺序结构&#xff0c;也可以用链式结构。本篇我们来学习顺序结构的二叉树&#xff0c;起个新名字—…

数据结构系列一:初识集合框架+复杂度

前言 数据结构——是相互之间存在一种或多种特定关系的数据元素的集合。数据结构是计算机专业的基础课程&#xff0c;但也是一门不太容易学好的课&#xff0c;它当中有很多费脑子的东西&#xff0c;之后在学习时&#xff0c;你若碰到了困惑或不解的地方 都是很正常的反应&…

Python 入门教程(2)搭建环境 | 2.3、VSCode配置Python开发环境

文章目录 一、VSCode配置Python开发环境1、软件安装2、安装Python插件3、配置Python环境4、包管理5、调试程序 前言 Visual Studio Code&#xff08;简称VSCode&#xff09;以其强大的功能和灵活的扩展性&#xff0c;成为了许多开发者的首选。本文将详细介绍如何在VSCode中配置…

VSCode自定义快捷键和添加自定义快捷键按键到状态栏

VSCode自定义快捷键和添加自定义快捷键按键到状态栏 &#x1f4c4;在VSCode中想实现快捷键方式执行与某些指令操作进行绑定&#xff0c;可以通过配置组合式的键盘按键映射来实现&#xff0c;另外一种方式就是将执行某些特定的指令嵌入在面板菜单上&#xff0c;在想要执行的时候…

Linux系统安装MySQL5.7(其他版本类似)避坑指南

1.远程连接 在Linux系统安装好MySQL5.7数据库&#xff0c;不要以为就大功告成了后面还有大坑等着你踩了。宏哥这里介绍一下远程连接遇到的坑以及如何处理。由于征文要求安装环境教学除外宏哥这里就不介绍在Linux系统安装mysql数据库&#xff0c;有需要的可以自己百度一下。但是…

HybridCLR+Adressable+Springboot热更

本文章会手把手教大家如何搭建HybridCLRAdressableSpringboot热更。 创作不易&#xff0c;动动发财的小手点个赞。 安装华佗 首先我们按照官网的快速上手指南搭建一个简易的项目&#xff1a; 快速上手 | HybridCLR 注意在热更的代码里添加程序集。把用到的工具放到程序集里…

C语言(12)--------->for循环

在C语言中&#xff0c;有三大结构&#xff1a;顺序、选择、循环。这些结构可以用于处理生活中各种各样的复杂问题。选择结构通常是用if语句或者switch语句实现&#xff0c;可参考前面的博客&#xff1a; C语言&#xff08;7&#xff09;------------&#xff1e;if语句CSDN C…

react路由总结

目录 一、脚手架基础语法(16~17) 1.1、hello react 1.2、组件样式隔离(样式模块化) 1.3、react插件 二、React Router v5 2.1、react-router-dom相关API 2.1.1、内置组件 2.1.1.1、BrowserRouter 2.1.1.2、HashRouter 2.1.1.3、Route 2.1.1.4、Redirect 2.1.1.5、L…

JAVA最新版本详细安装教程(附安装包)

目录 文章自述 一、JAVA下载 二、JAVA安装 1.首先在D盘创建【java/jdk-23】文件夹 2.把下载的压缩包移动到【jdk-23】文件夹内&#xff0c;右键点击【解压到当前文件夹】 3.如图解压会有【jdk-23.0.1】文件 4.右键桌面此电脑&#xff0c;点击【属性】 5.下滑滚动条&…

拆解微软CEO纳德拉战略蓝图:AI、量子计算、游戏革命如何改写未来规则!

2025年2月19日 知名博主Dwarkesh Patel对话微软CEO萨蒂亚纳德拉 在最新访谈释放重磅信号&#xff1a;AI将掀起工业革命级增长&#xff0c;量子计算突破引爆材料科学革命&#xff0c;游戏引擎进化为世界模拟器。 整个视频梳理出几大核心观点&#xff0c;揭示科技巨头的未来十年…

记录此刻:历时两月,初步实现基于FPGA的NVMe SSD固态硬盘存储控制器设计!

背景 为满足实验室横向项目需求&#xff0c;在2024年12月中下旬导师提出基于FPGA的NVMe SSD控制器研发项目。项目核心目标为&#xff1a;通过PCIe 3.0 x4接口实现单盘3000MB/s的持续读取速率。 实现过程 调研 花了半个月的时间查阅了一些使用FPGA实现NVME SSD控制器的论文、…

Grok 3与GPT-4.5的“智能天花板”争夺战——谁才是大模型时代的算力之王?

2025年2月18日&#xff0c;马斯克旗下 xAI 高调发布新一代大模型Grok 3&#xff0c;号称“地球上最聪明AI”&#xff0c;在数学推理、代码生成等核心能力上碾压 GPT-4o、DeepSeek-V3 等对手。而就在同一天&#xff0c;OpenAI创始人 Sam Altman 暗示 GPT-4.5 即将登场&#xff0…