C++ 11 多线程 (一)

C++ 11 多线程 (一)

  1. thread 类
  2. 方法列表
函数名形式备注
构造函数-默认构造函数thread()
-初始化构造函数thread(Fn&& fn, Args&&… args);
-移动构造函数thread (thread&& x) noexcept;
无法使用拷贝构造函数
析构器~thread()
=-move: thread& operator= (thread&& rhs) noexcept;-If the object is currently not joinable, it acquires the thread of execution represented by rhs (if any).
-If it is joinable, terminate() is called.
-After the call, rhs no longer represents any thread of execution (as if default-constructed).
-无法使用移动赋值
获取idget_id()若object目前是joinable,返回unique id
-若object目前不是joinable,返回thread::id的默认构造对象
查看join状态joinable()false reason:
-thread是默认构造,例如thread t;
-使用过移动构造函数或移动赋值
-使用了join() 或 detach()
join()-thread t(func);t.join();
-使用函数后,线程t变为non-joinable,t可以被安全的destroy
-调用t的线程阻塞,直到t返回
detach()与join()区别:调用t的线程执行与t的执行互相独立
其他-swap(thread& x)
-native_handle()
-static unsigned hardware_concurrency() noexcept;

  1. 代码实践
    3.1. join()方法
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <future>
#include <iostream>
#include <Windows.h>
using namespace std;
int Num = 100;
void thread_task1() {
    for (int i = 0; i < 5; ++i) {
        cout << "Thread_Task1: " << i << " Num is" << Num-- << endl;
        Sleep(100);
    }
}

void thread_task2() {
    for (int i = 0; i < 5; ++i) {
        cout << "Thread_Task2: " << i << " Num is" << Num-- << endl;
        Sleep(100);
    }
}

void main() {
    thread t1 = thread(thread_task1);
    cout << "t1 thread id is " << t1.get_id() << endl;
    thread t2 = thread(thread_task2);
    cout << "t2 thread id is " << t2.get_id() << endl;
    t1.join();
    t2.join();
    for (int i = 0; i < 5; ++i) {
        cout << "Main is working" << endl;
    }
    Sleep(10000);
    system("pause");
}

运行结果
这里写图片描述
从图中可以看到
- 创建thread后,会自动进入就绪状态
-调用join()前,主线程与t1,t2 独立运行
-为t1和t2均调用join()后,t1和t2的执行会阻塞main线程,但t1和t2不会互相阻塞
3.2 detach()方法
detach()与join()方法区别可见
https://stackoverflow.com/questions/22803600/when-should-i-use-stdthreaddetach
http://blog.csdn.net/joeblackzqq/article/details/42167617
detach()与join()的区别
-线程是否block调用主体。
-其次,join()提供了一种最直观的方法来保证线程的完成,而使用detach()方法需要额外提供方法来保证线程的完成
-两个方法是互斥使用的,使用任一方法后再使用另一方法会报异常。(使用join()后,线程已经执行完成,可能已被析构;使用detach()后,线程已经是non-joinable)
-线程创建后必须使用join或者detach,这两个方法可以保证调用线程的析构函数不会出现异常。否则程序结束(比如main返回)本地线程栈可能会被损坏,出现crash或者kill现象,导致出现一些异常:资源锁死,写到一半的文件=====

在std::thread的析构函数中,std::terminate会被调用如果:
-线程没有被Joined(用t.join())
-线程也没有被detached(用t.detach())
3.2 swap()方法

int Num = 100;
mutex m;
void thread_task1() {
    for (int i = 0; i < 5; ++i) {
        m.lock();
        cout << "Thread_Task1: " << i << " Num is" << Num-- << endl;
        m.unlock();
        Sleep(100);
    }
    for (int i = 0; i < 10000; ++i) Sleep(10);
}

void thread_task2() {
    for (int i = 0; i < 5; ++i) {
        m.lock();
        cout << "Thread_Task2: " << i << " Num is" << Num-- << endl;
        m.unlock();
        Sleep(100);
    }
}

void main() {
    thread t1 = thread(thread_task1);
    thread t2 = thread(thread_task2);
    cout << endl << endl << "Involve detach()" << endl << endl;
    t1.detach();
    t2.detach();
    thread t3 = thread([] {cout << "t3:Lambda creates thread"; });
    m.lock();
    cout << endl << "before swap" << endl;
    cout << "t1 thread id is " << t1.get_id() <<" t1.joinable() is " << t1.joinable() << endl;
    cout << "t3 thread id is " << t3.get_id() <<" t3.joinable() is " << t3.joinable() << endl;
    t1.swap(t3);
    cout << endl << "after swap" << endl;
    cout << "t1 thread id is " << t1.get_id() << " t1.joinable() is " << t1.joinable() << endl;
    cout << "t3 thread id is " << t3.get_id() << " t3.joinable() is " << t3.joinable() << endl;
    m.unlock();
    cout << endl <<endl << "Join the t1" << endl << endl;
    t1.join();
    for (int i = 0; i < 5; ++i) {
        cout << "Main is working" << endl;
        Sleep(100);
    }
    Sleep(1000);
    system("pause");
}

运行结果
这里写图片描述
-在这里使用swap方法交换了t1和t3的状态(包括id和joinable())
-t1可以再一次调用join方法(并且没有报异常),但是注意到join()方法并没有起作用。t3方法没有调用join()或detach(),并且结束后没有报异常(但是t3的这种方法是不安全的)
-使用mutex进行了互斥访问
-使用lamda创建了thread实例

此外,我们注意到thread id为0的情况,在这里0是默认线程id
当一个线程类实例的 get_id() 等于默认值的时候,即 get_id() == thread::id(),表示这个线程类实例处于下述状态之一:
-尚未指定运行的任务(thread t;)
-线程运行完毕
-线程已经被转移 (move) 到另外一个线程类实例
-线程已经被分离 (detached)

3.3 move方法

void main() {
    thread t1 = thread(thread_task1);
    thread t2 = thread(thread_task2);
    t1.join();
    t2.join();
    thread t3 = thread([](int n) {
        cout << "t3:Lambda creates thread"; 
        Sleep(n);
        cout << "Wake" << endl;
    },10000);
    //t1 = move(t3);//will lead to std::terminate()
    t3.detach();
    t1 = move(t3);//correct
    for (int i = 0; i < 5; ++i) {
        cout << "Main is working" << endl;
        Sleep(100);
    }
    Sleep(1000);
    system("pause");
}

move()方法的特点
-move()方法和swap()方法的区别是move()方法同时还会转移thread的实例内容
-t1 = move(t3)后,t3不在代表任何thread,在这之前若t3或者t1是joinable(),则将调用terminate()。
-执行move后,若原t1线程内实例未执行完成,则将继续执行,但是无法通过t1访问原线程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值