std::thread(线程)

本文介绍了C++中线程的基本创建方法及管理技巧,包括如何使用std::thread创建线程,如何确保线程正确执行完毕,以及如何通过mutex和lock_guard实现线程同步。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自https://www.cnblogs.com/whlook/p/6573659.html

创建线程

  • 创建线程比较简单,C++提供头文件thread,使用std的thread实例化一个线程对象创建。

  • std::thread 在 #include 头文件中声明,因此使用 std::thread 时需要包含 #include 头文件。

#include<iostream>
#include<thread>

using namespace std;

void thread1() {
    for(int i=0;i<20;++i)
        cout << "thread1..." << endl;
}

void thread2() {
    for (int i = 0; i<20; ++i)
        cout << "thread2..." << endl;
}

int main(int argc, char* argv[]) {
    thread th1(thread1);   //实例化一个线程对象th1,该线程开始执行
    thread th2(thread2);
    cout << "main..." << endl;
    return 0;
}
  • 结果分析:上例是有问题的,因为在创建了线程后线程开始执行,但是主线程main()并没有停止脚步,仍然继续执行然后退出,此时线程对象还是joinable(可结合的),线程仍然存在但指向它的线程对象已经销毁,所以会中断。

这里写图片描述

  • 那么该如何保证子线程执行完了退出后再退出主线程呢?

解决方法一:thread::join()

  • 使用join接口可以解决上述问题,join的作用是让主线程等待直到该子线程执行结束。
#include<iostream>
#include<thread>

using namespace std;

void thread1() {
    for(int i=0;i<20;++i)
        cout << "thread1..." << endl;
}

void thread2() {
    for (int i = 0; i<20; ++i)
        cout << "thread2..." << endl;
}

int main(int argc, char* argv[]) {
    thread th1(thread1);   //实例化一个线程对象th1,该线程开始执行
    thread th2(thread2);
    cout << "****************" << th1.joinable() << endl;  
    th1.join();
    cout << "****************" << th1.joinable() << endl;
    th2.join();
    cout << "main..." << endl;
    return 0;
}
  • 结果分析:此时就可以正常地执行子线程了,同时注意最后一个输出,说明了main是等待子线程结束才继续执行的。

  • 需要注意的是线程对象执行了join后就不再joinable(判断线程是否可以加入等待)了,所以只能调用join一次。

  • joinable: 检查线程是否可被 join。检查当前的线程对象是否表示了一个活动的执行线程,由默认构造函数创建的线程是不能被 join 的。另外,如果某个线程已经执行完任务,但是没有被 join 的话,该线程依然会被认为是一个活动的执行线程,因此也是可以被 join 的。

这里写图片描述

解决方法一:thread::detach()

  • 将当前线程对象所代表的执行实例与该线程对象分离,使得线程的执行可以单独进行。一旦线程执行完毕,它所分配的资源将会被释放。

  • detach是用来分离线程,这样线程可以独立地执行,不过这样由于没有thread对象指向该线程而失去了对它的控制,当对象析构时线程会继续在后台执行,但是当主程序退出时并不能保证线程能执行完。如果没有良好的控制机制或者这种后台线程比较重要,最好不用detach而应该使用join。

#include<iostream>
#include<thread>

using namespace std;

void thread1() {
    for(int i=0;i<20;++i)
        cout << "thread1..." << endl;
}

void thread2() {
    for (int i = 0; i<20; ++i)
        cout << "thread2..." << endl;
}

int main(int argc, char* argv[]) {
    thread th1(thread1);   //实例化一个线程对象th1,该线程开始执行
    thread th2(thread2);
    th1.detach();
    th2.detach();
    cout << "main..." << endl;
    return 0;
}
  • 结果分析:线程未执行完退出。

这里写图片描述

mutex

  • 头文件是,mutex是用来保证线程同步的,防止不同的线程同时操作同一个共享数据。

  • #include:该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock, 以及其他的类型和函数。

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

using namespace std;

mutex m;
int cnt = 10;

void thread1() {
    while (cnt > 5){
        m.lock();
        if (cnt > 0) {
            --cnt;
            cout << cnt << endl;
        }
        m.unlock();
    }
}

void thread2() {
    while (cnt > 0) {
        m.lock();
        if (cnt > 0) {
            cnt -= 10;
            cout << cnt << endl;
        }
        m.unlock();
    }
}

int main(int argc, char* argv[]) {
    thread th1(thread1);   //实例化一个线程对象th1,该线程开始执行
    thread th2(thread2);
    th1.join();
    th2.join();
    cout << "main..." << endl;
    return 0;
}
  • 结果分析:mutex是不安全的,当一个线程在解锁之前异常退出了,那么其它被阻塞的线程就无法继续下去

这里写图片描述

std::lock_guard

  • 使用lock_guard则相对安全,它是基于作用域的,能够自解锁,当该对象创建时,它会像m.lock()一样获得互斥锁,当生命周期结束时,它会自动析构(unlock),不会因为某个线程异常退出而影响其他线程。
#include<iostream>
#include<thread>
#include<mutex>

using namespace std;

mutex m;
int cnt = 10;

void thread1() {
    while (cnt > 5){
        lock_guard<mutex> lockGuard(m);
        if (cnt > 0) {
            --cnt;
            cout << cnt << endl;
        }
    }
}

void thread2() {
    while (cnt > 0) {
        lock_guard<mutex> lockGuard(m);
        if (cnt > 0) {
            cnt -= 10;
            cout << cnt << endl;
        }
    }
}

int main(int argc, char* argv[]) {
    thread th1(thread1);   //实例化一个线程对象th1,该线程开始执行
    thread th2(thread2);
    th1.join();
    th2.join();
    cout << "main..." << endl;
    return 0;
}
  • 结果

这里写图片描述

其他

  • get_id: 获取线程 ID,返回一个类型为 std::thread::id 的对象。

  • swap():交换两个线程对象所代表的底层句柄(underlying handles)。

  • 拷贝构造函数是被禁用的:thread (const thread&) = delete;

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

using namespace std;

mutex m;
int cnt = 10;

void thread1() {
    while (cnt > 5){
        lock_guard<mutex> lockGuard(m);
        if (cnt > 0) {
            --cnt;
            cout << cnt << endl;
        }
    }
}

void thread2() {
    while (cnt > 0) {
        lock_guard<mutex> lockGuard(m);
        if (cnt > 0) {
            cnt -= 10;
            cout << cnt << endl;
        }
    }
}

int main(int argc, char* argv[]) {
    thread th1(thread1);   //实例化一个线程对象th1,该线程开始执行
    thread th2(thread2);
    thread th3(th1);       //拷贝构造函数被删除
    cout << "******" << "th1 id: " << th1.get_id() << "******" << endl;
    cout << "******" << "th2 id: " << th2.get_id() << "******" << endl;
    th1.swap(th2);
    cout << "******" << "th1 id: " << th1.get_id() << "******" << endl;
    cout << "******" << "th2 id: " << th2.get_id() << "******" << endl;
    th1.join();
    th2.join();

    cout << "main..." << endl;
    return 0;
}
  • 结果

这里写图片描述

### C++ `std::thread` 的线程安全退出方式 在 C++ 中,`std::thread` 提供了多种方法来处理线程的生命周期管理,其中包括 `join()` 和 `detach()` 方法。为了确保程序的安全性和资源的有效释放,理解这些方法的行为至关重要。 #### 使用 `join()` 安全退出 调用 `join()` 是一种同步的方式,用于等待当前线程完成其执行过程后再继续主线程的操作。如果一个线程对象处于可连接状态 (`joinable() == true`) 并未显式调用 `join()` 或 `detach()` 就销毁该对象,则会触发异常行为(通常抛出 `std::terminate`)。因此,在大多数情况下推荐使用 `join()` 来结束线程[^1]。 ```cpp #include <iostream> #include <thread> void threadFunction() { std::cout << "Thread is running\n"; } int main() { std::thread t(threadFunction); if (t.joinable()) { // 检查线程是否可以加入 t.join(); // 等待子线程完成 } return 0; } ``` #### 使用 `detach()` 非阻塞退出 另一种让线程独立运行而不与原始线程绑定的方法是调用 `detach()` 函数。一旦调用了此操作,线程将继续在其自己的上下文中执行直到自然终止或者被其他机制停止。需要注意的是,对于已分离的线程来说,程序员无法再控制它的生命期或获取返回值等信息[^2]。 ```cpp #include <iostream> #include <thread> void detachedThreadFunction() { std::this_thread::sleep_for(std::chrono::seconds(2)); std::cout << "Detached Thread has finished its work.\n"; } int main(){ std::thread t(detachedThreadFunction); if(t.joinable()){ t.detach(); // 让线程自行运行并最终销毁自己 } std::cout << "Main continues execution immediately after detaching the thread.\n"; return 0; // 主函数在此处正常结束, 不影响detached线程 } ``` #### 异常情况下的强制终止 - `std::terminate` 尽管不建议主动调用 `std::terminate` 终止整个应用程序作为常规错误处理手段,但在某些极端条件下比如检测到严重逻辑错误时可能会考虑这样做。然而更佳实践应当始终尝试优雅地关闭所有活动组件而不是简单地中止进程。 --- ### 总结 - **Join**: 推荐做法,保证资源清理以及顺序一致性。 - **Detach**: 当不需要进一步交互时适用,但需注意潜在风险。 - **Terminate**: 只应在无可挽回的情况下才采用。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值