C++11中引入的thread

第一部分:thread概念

一、引言

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

二、基本概念

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

三、构造函数

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

四、示例

cpp 复制代码
#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对象

cpp 复制代码
#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 是最基本的同步原语,它提供了一种保护共享数据免受多个线程同时访问的机制。互斥锁通过锁定和解锁操作来确保在任一时刻只有一个线程可以访问受保护的数据。

cpp 复制代码
#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 是一个作用域锁定的封装,它会在构造时自动锁定互斥锁,并在作用域结束时自动解锁。这有助于减少因忘记释放锁而导致的死锁问题。

cpp 复制代码
#include <mutex>  
 
std::mutex mtx;  
 
void safe_increment() {  
    std::lock_guard<std::mutex> guard(mtx);  
    ++shared_data; // mtx 已被锁定  
}
3. 条件变量(Condition Variables):

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

cpp 复制代码
#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 关键字来实现。

cpp 复制代码
#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新增的其他性

相关推荐
秃头佛爷6 分钟前
Python学习大纲总结及注意事项
开发语言·python·学习
待磨的钝刨7 分钟前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师3 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉4 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer4 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq4 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
青花瓷5 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode