当前位置: 首页 > news >正文

C++11中引入的thread

第一部分:thread概念

一、引言

        C++11 引入了多线程编程的支持,主要是通过 <thread> 头文件中的 std::thread 类来实现的。这一特性极大地增强了 C++ 应用程序的并发处理能力,允许开发者编写能够同时执行多个任务的应用程序。

二、基本概念

        std::thread 是一个表示执行线程的类,它提供了创建和管理线程的基本功能。每个 std::thread 对象都与一个执行线程相关联。当 std::thread 对象被创建时,它会启动一个新的执行线程,该线程会执行传递给 std::thread 构造函数的函数或可调用对象。

三、构造函数

        std::thread 类有几个构造函数,但最常用的构造函数接受一个可调用对象(如函数指针、lambda 表达式、函数对象、绑定表达式等)作为参数,并可选地接受一个或多个传递给该可调用对象的参数。

四、示例

#include <iostream>  
#include <thread>  void threadFunction(int n) {  std::cout << "Thread function called with n = " << n << std::endl;  
}  int main() {  std::thread t(threadFunction, 42); // 创建一个新线程,运行 threadFunction(42)  t.join(); // 等待线程结束  return 0;  
}

五、成员函数

  • join(): 阻塞当前线程,直到与之关联的线程执行完毕。
  • detach(): 将线程与 std::thread 对象分离,允许线程独立执行。一旦分离,std::thread 对象将不再拥有任何线程,也不能再次与任何线程关联或加入。
  • get_id(): 返回线程的标识符(std::thread::id 类型)。
  • joinable(): 检查线程是否可被 join()。如果线程已经执行完毕或被分离,则返回 false;否则返回 true。
  • swap(std::thread& other): 交换两个 std::thread 对象的线程所有权。

六、注意事项

  • 资源管理:如果不调用 join() 或 detach(),std::thread 对象的析构函数会调用 std::terminate(),因为此时线程仍在运行且没有明确的处理方式。
  • 线程安全:多线程程序必须考虑数据竞争和同步问题,可能需要使用互斥锁(std::mutex)、条件变量(std::condition_variable)等同步机制。
  • 性能考量:线程的创建和销毁是昂贵的操作,应尽量减少不必要的线程创建和销毁。
  • 异常安全:如果线程函数抛出异常且未被捕获,则标准没有规定具体的行为。因此,在线程函数中应妥善处理异常。

七、多个thread对象

#include <iostream>  
#include <thread>  
#include <vector>  void doWork(int id) {  std::cout << "Thread " << id << " is running" << std::endl;  
}  int main() {  std::vector<std::thread> threads;  for (int i = 0; i < 5; ++i) {  threads.emplace_back(doWork, i);  }  for (auto& t : threads) {  t.join();  }  return 0;  
}

        这个示例展示了如何创建多个线程来并行执行相同的任务。每个线程都被加入到 std::vector<std::thread> 容器中,并在之后通过循环调用 join() 等待所有线程完成。


第二部分:多线程编程相关的特性

C++11 中的 std::thread 以及其他与多线程编程相关的特性

一、线程同步

        在多线程编程中,线程同步是一个重要的概念,它用于控制多个线程之间的执行顺序,以避免数据竞争和其他并发问题。C++11 提供了几种线程同步机制:

1. 互斥锁(Mutexes):
        std::mutex 是最基本的同步原语,它提供了一种保护共享数据免受多个线程同时访问的机制。互斥锁通过锁定和解锁操作来确保在任一时刻只有一个线程可以访问受保护的数据。
#include <iostream>  
#include <thread>  
#include <mutex>  std::mutex mtx;  
int shared_data = 0;  void increment() {  mtx.lock(); // 锁定互斥锁  ++shared_data;  mtx.unlock(); // 解锁互斥锁  
}  int main() {  std::thread t1(increment);  std::thread t2(increment);  t1.join();  t2.join();  std::cout << "Shared data: " << shared_data << std::endl;  return 0;  
}

        注意:频繁地锁定和解锁互斥锁可能会降低性能,因为线程需要等待锁变为可用。

2. 锁保护(Lock Guards):
        std::lock_guard 是一个作用域锁定的封装,它会在构造时自动锁定互斥锁,并在作用域结束时自动解锁。这有助于减少因忘记释放锁而导致的死锁问题。
#include <mutex>  std::mutex mtx;  void safe_increment() {  std::lock_guard<std::mutex> guard(mtx);  ++shared_data; // mtx 已被锁定  
}
3. 条件变量(Condition Variables):


        std::condition_variable 用于阻塞一个或多个线程,直到接收到另一个线程的通知。它通常与互斥锁一起使用,以安全地等待某个条件变为真。

#include <condition_variable>  
#include <mutex>  
#include <thread>  std::mutex mtx;  
std::condition_variable cv;  
bool ready = false;  void print_id(int id) {  std::unique_lock<std::mutex> lck(mtx);  while (!ready) cv.wait(lck); // 等待 ready 变为 true  std::cout << "Thread " << id << '\n';  
}  void go() {  std::unique_lock<std::mutex> lck(mtx);  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(); // 允许 10 个线程继续执行  for (auto& th : threads) th.join();  return 0;  
}
4. 线程局部存储(Thread-Local Storage, TLS)


        C++11 引入了线程局部存储的概念,允许变量在每个线程中都有自己的实例。这可以通过 thread_local 关键字来实现。

#include <iostream>  
#include <thread>  thread_local int tls_counter = 0;  void print_and_increment() {  ++tls_counter;  std::cout << "tls_counter = " << tls_counter << std::endl;  
}  int main() {  std::thread t1(print_and_increment);  std::thread t2(print_and_increment);  t1.join();  t2.join();  // 主线程也有自己的 tls_counter 实例  print_and_increment();  return 0;  
}

        在这个例子中,每个线程(包括主线程)都有自己的 tls_counter 实例,并且它们之间的值互不影响。

二、小结

        C++11 通过 std::thread 和其他相关的类和函数(如 std::mutex、std::lock_guard、std::condition_variable 和 thread_local)为多线程编程提供了强大的支持。然而,编写正确的多线程代码仍然是一个挑战,需要开发者深入理解线程同步和并发编程的概念。

附:c++11新增的其他性

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • c语言中“sizeof”和“strlen”的区别
  • linux atomic 原子变量操作
  • 【数列求值 / B】
  • Parallels Desktop 20(Mac虚拟机) v20.0.0 for Mac 最新破解版(支持M系列)
  • 【tomcat】tomcat学习笔记
  • 阿里云 Quick BI使用介绍
  • 基于SAM大模型的遥感影像分割工具,用于创建交互式标注、识别地物的能力,可利用Flask进行封装作为Web后台服务
  • 利用H5无插件播放RTSP流的实现方案
  • 【二等奖论文】2024年华为杯研究生数学建模F题成品论文(后续会更新)
  • 搜维尔科技:Unity中的A.R.T.测量工具
  • Spring Cloud Alibaba-(4)Sentinel【流控和降级】
  • C# 入坑JAVA 潜规则 大小写敏感文件名和类名 枚举等 入门系列2
  • 策略模式在 Spring Boot 框架中的应用
  • 实验3 Hadoop集群运行环境搭建和使用
  • 创建索引遇到这个Bug,19c中还没有修复
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • 收藏网友的 源程序下载网
  • Angular 2 DI - IoC DI - 1
  • cookie和session
  • Java读取Properties文件的六种方法
  • Python利用正则抓取网页内容保存到本地
  • scala基础语法(二)
  • webgl (原生)基础入门指南【一】
  • 包装类对象
  • 记一次和乔布斯合作最难忘的经历
  • 马上搞懂 GeoJSON
  • 如何实现 font-size 的响应式
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 学习使用ExpressJS 4.0中的新Router
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 容器镜像
  • #Linux(权限管理)
  • #stm32整理(一)flash读写
  • $forceUpdate()函数
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (152)时序收敛--->(02)时序收敛二
  • (3)医疗图像处理:MRI磁共振成像-快速采集--(杨正汉)
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (python)数据结构---字典
  • (vue)el-tabs选中最后一项后更新数据后无法展开
  • (补充):java各种进制、原码、反码、补码和文本、图像、音频在计算机中的存储方式
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (一)Thymeleaf用法——Thymeleaf简介
  • (转)德国人的记事本
  • .java 9 找不到符号_java找不到符号
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始
  • .Net(C#)自定义WinForm控件之小结篇
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • [ vulhub漏洞复现篇 ] ECShop 2.x / 3.x SQL注入/远程执行代码漏洞 xianzhi-2017-02-82239600
  • [2015][note]基于薄向列液晶层的可调谐THz fishnet超材料快速开关——