C++ 多线程thread

0、多线程接口介绍

  1. 不同平台的多线程API, 比如unix下有pthread,,win下有_beginthreadex()。
  2. std::thread是属于C++自带的多线程接口,还有boost::blind也是C++自带的多线程接口

boost是C++的测试版本,许多C++的新特性会在这里测试,待时机成熟后悔合并到C++中。boost的C++特性也是稳定的,但不是C++自带,需要编译成库引用。

1、 std::thread基本介绍

1.1两种状态

一个std::thread对象只可能处于可联结或不可联结两种状态之一:

  • 可联结:当线程己运行或可运行、或处于阻塞时是可联结的。注意,如果某个底层线程(比如主线程)已经执行完任务,但是没有被join的话,仍然处于joinable状态,这时如果退出主线程,因为该子线程还未join仍处于可联结状态,会出现段错误。 即std::thread对象(对象由父线程所有)与底层线程保持着关联时,为joinable状态。
  • 不可联结:
    1. 当不带参构造的std::thread对象为不可联结,因为底层线程还没创建。
    2. 己移动的std::thread对象为不可联结。
    3. 己调用join或detach的对象为不可联结状态。因为调用join()以后,底层线程己结束,而detach()会把std::thread对象和对应的底层线程之间的连接断开。

综上:std::thread对象析构时,会先判断是否可joinable(),如果可联结,则程序会直接被终止出错。这意味着创建thread对象以后,必须要在随后的某个地方调用join或detach以便让std::thread处于不可联结状态。

1.2 join和detach

join():等待子线程执行,调用线程(如主线程)处于阻塞。join()执行完成之后,底层线程id被设置为0,即joinable()变为false。

detach():分离子线程,与当前线程的连接被断开,joinable()变为false,子线程成为后台线程,被C++运行时库接管。此时子线程与主线程并行运行。不过这样由于没有thread对象指向该线程而失去了对它的控制,当对象析构时线程会继续在后台执行,但是当主程序退出时并不能保证线程能执行完(退出thread_func即是执行完子线程)。如果没有良好的控制机制或者这种后台线程比较重要,最好不用detach而应该使用join。

2、C++ demo

2.1 CMakeLists.txt
# 声明要求的 cmake 最低版本
cmake_minimum_required(VERSION 3.2)

# 声明一个 cmake 工程
project(thread_demo)

add_definitions(-std=c++11)

# 添加一个可执行程序
# 语法: add_executable( 程序名 源代码文件 )

add_executable(thread_demo src/main.cpp)

include_directories(
    ${PROJECT_SOURCE_DIR}
    ${PROJECT_SOURCE_DIR}/include
)

target_link_libraries(thread_demo pthread)#thread库
2.2 main.cpp
#include<iostream>
#include<thread>

using namespace std;

bool close_thread = false;//设置一个标志位,用于退出子线程

void subThread() {
    while(1){
        if (close_thread)
            break;//跳出函数,或者执行完函数,就认为子线程接收
        std::cout << "sub thread" << std::endl;
    }
}


int main(int argc, char* argv[]) {
    thread sub_thread(subThread);   //实例化一个线程对象sub_thread,该线程开始执行
    
    std::cout << "main thread" << std::endl;
    
    close_thread = true;//告诉要退出程序了,子线程也要退出
    sub_thread.join();  //使不可联结

    return 0;
}

3、线程安全

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

3.1 mutex

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

#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是不安全的,当一个线程在解锁之前异常退出了,那么其它被阻塞的线程就无法继续下去,因为mutex还是保持锁着的状态。

3.2 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值