C++ thread 基本用法

1.thread类定义

表示各个执行线程的类。一个初始化的线程对象表示活动的执行线程; 这样的线程对象是可连接的,并且具有唯一的线程ID。

缺省构造(未初始化)的线程对象不可join,并且其线程ID对于所有不可join的线程都是通用的。C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。

2.构造函数

转自: https://blog.csdn.net/coolwriter/article/details/79883253

(1).默认构造函数,创建一个空的 thread 执行对象。
(2).初始化构造函数,创建一个 thread 对象,该 thread 对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3).拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4).move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
例子:

#include<thread>  
#include<chrono>  
using namespace std;  
void fun1(int n)  //初始化构造函数  
{  
    cout << "Thread " << n << " executing\n";  
    n += 10;  
    this_thread::sleep_for(chrono::milliseconds(10));  
}  
void fun2(int & n) //拷贝构造函数  
{  
    cout << "Thread " << n << " executing\n";  
    n += 20;  
    this_thread::sleep_for(chrono::milliseconds(10));  
}  
int main()  
{  
    int n = 0;  
    thread t1;               //t1不是一个thread  
    thread t2(fun1, n + 1);  //按照值传递  
    t2.join();  
    cout << "n=" << n << '\n';  
    n = 10;  
    thread t3(fun2, ref(n)); //引用  
    thread t4(move(t3));     //t4执行t3,t3不是thread  
    t4.join();  
    cout << "n=" << n << '\n';  
    return 0;  
}  
运行结果:  
Thread 1 executing  
n=0  
Thread 10 executing  
n=30
#include<thread>  
#include<chrono>  
using namespace std;  
void fun1(int n)  //初始化构造函数  
{  
    cout << "Thread " << n << " executing\n";  
    n += 10;  
    this_thread::sleep_for(chrono::milliseconds(10));  
}  
void fun2(int & n) //拷贝构造函数  
{  
    cout << "Thread " << n << " executing\n";  
    n += 20;  
    this_thread::sleep_for(chrono::milliseconds(10));  
}  
int main()  
{  
    int n = 0;  
    thread t1;               //t1不是一个thread  
    thread t2(fun1, n + 1);  //按照值传递  
    t2.join();  
    cout << "n=" << n << '\n';  
    n = 10;  
    thread t3(fun2, ref(n)); //引用  
    thread t4(move(t3));     
    t4.join();  
    cout << "n=" << n << '\n';  
    return 0;  
}  
运行结果:  
Thread 1 executing  
n=0  
Thread 10 executing  
n=30

其中t3的move,移动构造函数会将 t3 持有的线程句柄转移到 t4,并将 t3 标记为没有关联任何线程的状态。t3 在移动操作之后变为不可 joinable 的状态,因为它不再持有线程的所有权。试图使用 t3 再进行操作(如 joindetach)将导致未定义行为。

  • 原来由 t3启动的线程会继续执行 someFunc,但现在这个线程的所有权属于 t4
  • 由于线程是在 t3 创建时启动的,移动操作不会重新启动或中断线程,线程继续执行 someFunc,只是现在 t4 是它的控制对象。

3.其他成员函数

 4.join和detach

可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached。用于thread对象的生命周期管理。

在 C++ 中,线程(std::thread 对象)必须在销毁之前被主线程 join 或者设置为 detached,这是因为如果不这样做,程序可能会出现资源泄漏或者未定义行为。每个 std::thread 对象都会占用一定的系统资源(如线程句柄、堆栈空间等)。如果一个 std::thread 对象在销毁时仍然是 joinable 的,并且没有被 joindetach,这将导致程序的未定义行为。这是因为 C++ 线程库没有明确指定在这种情况下应该执行的操作。

join: 主线程等待子线程完成执行。当主线程调用子线程的 join 方法时,主线程将阻塞,直到子线程完成。这确保了子线程的资源能够被正确回收。

std::thread t(some_function);
t.join(); // 主线程等待 t 线程完成

 detach: 将子线程与主线程分离,让子线程在后台独立运行。当主线程调用子线程的 detach 方法时,子线程将独立执行,主线程不会等待子线程完成。分离的线程在完成后会自动释放其资源。

std::thread t(some_function);
t.detach(); // t 线程在后台运行,主线程不再等待

错误用法:

int main() {
    std::thread t(thread_function);
    // 未调用 join 或 detach,可能导致未定义行为
    return 0;
}

当调用 detach 时,线程会自动释放资源,并不会导致资源泄露。分离的线程在其生命周期结束时,由操作系统或线程库负责清理其资源。

 

posted @ 2020-10-27 22:24  lypbendlf  阅读(565)  评论(0编辑  收藏  举报