编程语言
首页 > 编程语言> > C++11笔记-多线程-低层接口Thread

C++11笔记-多线程-低层接口Thread

作者:互联网

C++11笔记-多线程-初识高级接口async()和Future中认识了C++11多线程编程的高级接口以及简单的使用;
除了高级接口async和future,C++标准库还提供了一个启动及处理线程的底层接口;
下面就开始今天的学习笔记:

Thread

std::thread的对象是用来启动和表现线程。这些对象和操作系统提供的线程呈现一对一映射关系。
thread object和线程之间的关联始于将一个callable object指派给thread object作为初值(或是move/copy assign)给它,并夹带可能有的实参。这个关联的结束会有两种不同的情况:1.由join()等待线程成果或者结束;2.由detach()分离线程和主线程之间关联,主线程将失去thread object关联的线程,即thread object表示的线程对象不受任何人控制;不论哪一个函数都必须在thread object生命结束前或在一个新的thread object被move assigned之前被调用,否则程序就会因std::terminate()而中止;
如果thread object关联至某个线程,它就是所谓join able(可连接的)。在此情况下调用joinable()会获得true,调用get_id()会获得thread ID,其值不同于std::thread::id()所得。std::thread::id的构造函数会产生一个独一无二的ID用以表示“非线程”。如果没有任何线程被关联,调用thread::get_id()获得的便是那个特殊值,如果有关联某个线程,那么调用get_id()会获得独一无二thread ID;
关于thread ID:
1.唯一可对thread ID执行的操作就是对它们进行比较,或是将它们写至一个output stream。
2.一个已结束的线程的thread ID可被系统取回重复运用;

thread的操作函数

操作效果
thread tDefault构造函数,创建一个nonjoinable thread object
thread f(f,…)创建一个thread object,表示f将被启动于一个线程中(也许带有实参),或是抛出std::system_error
thread t(rv)Move 构造函数;创建一个新的thread object,取rv的状态并令rv变成nonjoinable
t.~thread()销毁*this,如果object是joinable则调用std::terminate()
t = rvMove assignment;将rv的状态move assign至t,如果t是joinable则调用std::terminate()
t.joinable()如果t有一个关联线程(也就是说它是joinable)便产出true
t.join()等待关联线程完成工作(如果该线程不是joinable便抛出std::system_error),然后令object变成nonjoinable
t.detach()解除t和线程之间的关联并且让线程继续运行(如果该线程不是joinable便抛出std::system_error),并令object变成nonjoinable
t.get_id()如果joinable就返回独一无二的std::thread::id,如果不是joinable就返回std::thread::id()
t.native_handle()返回一个依赖平台的类型native_handle_type,用于不具可移植性的扩展

除了上表中列举的接口外,thread还提供了一个static函数,用来查询并行线程的可能数量(只是一个参考值):
unsigned int std::thread::hardware_concurrency();
注意:如果线程数量不可计算或者不明确,hardware_concurrency返回值是0;

与async()相同之处

thread与async一样,可以传入任何callable object(可以是function、member function、function object、lambda),并可以夹带任何可能的实参。注意的是:除非你真的直到你在做什么,否则面对“处理目标函数所必须”的所有object都应该以by value方式传递,使得thread只使用local copy;

与async()不同之处

1.thread没有所谓的发射策略。C++标准库永远试着将目标函数启动与一个新线程中。如果无法做到就会抛出std::system_error并带着错误码resource_unavailable_try_again;
2.没有接口可以处理线程结束结果。唯一可以获取的是一个独一无二的线程ID;
3.如果发生异常,但未被捕捉于线程之内,程序会立刻中止并调用std::terminate()。如果想要将异常传播至线程外的某个context,必须使用exception_ptr;
4.在使用thread时,必须声明是否“想要等待线程结束”,即调用join();或者打算“将它从母体卸离(detach)使它运行于后台而不受任何控制”,就调用detach()。
5.如果让线程运行于后台而main()结束了,所有线程会被鲁莽而硬性地终止;

示例代码

#include <thread>
#include <chrono>
#include <random>
#include <exception>
#include <iostream>
using namespace std;

void doSomething(int num,char c)
{
    try
    {
        default_random_engine dre(42 * c);
        uniform_int_distribution<int> id(10, 1000);       
        for (size_t i = 0; i < num; i++)
        {
            this_thread::sleep_for(chrono::milliseconds(id(dre)));
            cout.put(c).flush();
        }
    }
    catch (const std::exception& e)
    {
        cerr << "THREAD-EXCEPTION (thead " 
             << this_thread::get_id() << "): " << e.what() << endl;
    }
    catch (...)
    {
        cerr << "THREAD-EXCEPTION (thead " << this_thread::get_id() << ")" << endl;
    }
}

int main()
{
    try
    {
        thread t1(doSomething,5,'.');
        cout << "- started fg thread " << t1.get_id() << endl;

        for (size_t i = 0; i < 5; i++)
        {
            thread t(doSomething, 10, 'a' + i);
            cout << "- detach started bg thread " << t.get_id() << endl;

            t.detach();
        }

        cin.get();
        cout << "- join fg thread " << t1.get_id() << endl;
        t1.join();
    }
    catch (const std::exception& e)
    {
        cerr << "EXCEPTION: " << e.what() << endl;
    }
}

当等待每个线程都执行完成后,按下Return键;
在这里插入图片描述
当程序刚开始运行,快速按下Return键,则main函数运行结束,程序会立刻终止所有后台线程;
可能会出现下图结果:
在这里插入图片描述

有点危险的Detached Thread

Detached Thread(卸离后的线程)很容易形成问题——如果它们使用nonloacl资源的话。
由于程序员失去了对detached thread的控制,没有轻松的办法可以得知它是否运行,以及运行多久;所以,绝对不要让一个detached thread访问任何寿命已结束的object。基于这个理由,“以by reference方式传递变量和object”给线程,总是带有风险,强烈建议使用by value的方式传递;
同理,如果程序中使用了全局变量和静态变量,那么不能确保在主线程退出时,detached thread线程没有在使用这些变量;很可能运行着的detached thread仍有可能在访问“已被销毁”或“正在析构”的全局变量和静态变量,这样也会产生不可预期的行为;
列举以下detached thread的一般性规则:
1.确保这些全局变量和静态变量在“对它们进行访问”之所有detached thread都结束之前不被销毁;
使用条件变量(condition variable),它让detached thread用来发信号说它们已结束,main函数可以销毁或者析构这些变量
2.以调用quick_exit()的方式结束程序;

文献

1.《C++标准库》第二版;

标签:11,std,Thread,thread,object,joinable,线程,多线程,id
来源: https://blog.csdn.net/liushao1031177/article/details/116888173