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

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新增的其他性


http://www.kler.cn/news/321707.html

相关文章:

  • 正向科技|格雷母线定位系统的设备接线安装示范
  • 脚手架是什么?详细版+通俗易懂版!!!!!!
  • DNS与host文件
  • 职业技能大赛-自动化测试笔记(PageObject)分享-4
  • 如何将自定义支付网关与 WooCommerce Checkout 区块集成
  • HarmonyOS---权限和http/Axios网络请求
  • 处理 VA02修改行项目计划行(SCHEDULE LINES )报错:不可能确定一个消耗帐户
  • count(1)、count(*) 与 count(列名) 的区别
  • zabbix“专家坐诊”第257期问答
  • 19、网络安全合规复盘
  • C++ | Leetcode C++题解之第440题字典序的第K小数字
  • 【HDP】zookeeper未授权漏洞修复
  • C语言课程设计题目四:实验设备管理系统设计
  • Flutter鸿蒙化环境配置(windows)
  • 网站设计中安全方面都需要有哪些考虑
  • 【opencv】——为arm平台交叉编译
  • Apache Iceberg 数据类型参考表
  • URL中 / 作为字符串,而不是路径。
  • 19.1 使用k8s的sdk编写一个项目获取pod和node信息
  • 【毕业论文+源码】如何使用Spring Boot搭建一个简单的篮球论坛系统
  • 企业急于采用人工智能,忽视了安全强化
  • Linux云计算 |【第四阶段】NOSQL-DAY3
  • ubuntu 24搭建docker私有仓库
  • 【图像处理】多幅不同焦距的同一个物体的平面图象,合成一幅具有立体效果的单幅图像原理(二)
  • 通过python脚本采集TCP自定义端口连接数数据推送到Prometheus
  • 【azure-openai】批量翻译demo【python】【gradio】
  • 流浪软件uniaccess agent 删除
  • Webpack、Rollup、Parcel 和 Grunt、Gulp 的区别
  • 【理解 Java 中的 for 循环】
  • 【RabbitMQ 项目】服务端:信道模块