文章目录
  1. 1. std::thread
    1. 1.0.1. Hello World
    2. 1.0.2. 成员函数
  • 2. namespace this_thread
  • 对于并发编程,首先应该从线程说起,对于线程的基本概念就不多说了,有不清楚的同学可以查查相关资料。这里就从< thread>开始看起,先对C++11的线程有个大致了解。

    std::thread

    一个thread对象也许没有表示任何线程,比如在默认构造后、move from后、detach、join后;一个在运行的线程也不一定有与之关联的thread对象,比如在detach后。不会出现两个thread对象表示着同一个执行的线程,于是thread是不可拷贝构造和拷贝赋值的,但是可以移动构造和移动赋值。

    Hello World

    std::thread的用法与pthread的类似,先从一个简单的例子看起:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include <iostream>
    #include <thread>


    int main(int argc, char** argv) {

    std::thread t{

    [](){

    std::cout << "Hello World!" << std::endl;

    }

    }



    t.join();

    return 0;

    }

    这里使用clang来编译,注意要加上 -std=c++11

    1
    2

    clang -std=c++11 -o helloWorld helloWorld.cpp

    这段代码里使用一个打印hello world的lambda表达式作为参数构造一个thread(当然,传递一个函数(指针)也是没有问题的),然后调用join()等待线程运行完毕。

    成员函数

    • 构造函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    thread( ); // (1) 默认构造

    thread( thread&& other ); // (2) 移动构造

    template< class Function, class... Args >

    explicit thread( Function&& f, Args&&... args ); // (3) 最常用的构造方法

    thread( const thread& ) = delete; // (4) 拷贝构造设置为delete的

    1) 创建了一个thread对象,没有表示任何线程

    2) 移动构造函数。创建一个表示other表示的线程的thread对象,构造完成后other不在表示之前的线程了

    3) 创建一个表示一个运行线程的thread对象。首先,构造函数通过使用下面的函数拷贝/移动所有的参数(连同函数f和它的参数一起)到线程可以访问的存储区:

    1
    2
    3
    4
    5
    6
    7
    8

    template <class T>

    typename decay<T>::type decay_copy(T&& v) {

    return std::forward<T>(v);

    }

    需要注意的是在此期间抛出的所有异常都会抛在当前线程,而不是新创建的线程里。

    下面来看下新的线程将会怎么去处理传入的参数。我们设value_args(包含函数和它参数一起的参数)为t1,t2,…tN,N为sizeof…(value_args),value_args是通过调用上面的decay_copy得到的。然后会按照下面的规则来执行代码:

    1) 如果f是类T的成员函数的指针,就会执行f,会忽略掉返回值。代码实际上是这样执行的:

    1
    2
    3
    4
    5
    6
    7
    8

    // 如果t1的类型是T,或者T的引用,或者从T派生出来类型的引用

    (t1.*f) (t2, ... , tN)

    // 否则

    ((*t1).*f) (t2, ..., tN)

    2) 如果N == 1,f是一个类的成员数据对象(member data object),则它会被访问。但是这个对象的值会被忽略。代码实际上是这样执行的:

    1
    2
    3
    4
    5
    6
    7
    8

    // 如果t1是T或者T的引用或者从T派生出来类型的引用

    t1.*f

    // 否则

    (*t1).*f

    3) 否则的话,f会被当做一个非成员函数来调用,返回值会被忽略。代码实际上是这样执行的:

    1
    2

    f(t1, t2, ..., tN)

    4) 拷贝构造函数是delete的,线程是不可拷贝的。没有两个thread对象表示着同一个线程。

    • Observers
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    //检查这个thread对象是否是有效的执行线程。如果get_id() != std::thread::id()就会返回true。默认构造的thread对象不是joinable 的。

    //如果一个线程执行完毕,但还没有被join,它依然被认为是有效的执行线程

    bool joinable() const;

    // 返回当前线程的id

    std::thread::id get_id() const;

    //返回native_handle

    native_handle_type native_handle();

    // 返回当前电脑所能支持的并发线程数量

    static unsigned hardware_concurrency();
    • Operations
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    // 阻塞当前线程,直到*this线程执行完毕

    void join();

    //将执行的线程与thread对象分离,允许线程独立的执行。任何分配的资源都会在线程退出时被释放

    // 在调用detach后,*this不在拥有任何线程

    void detach();

    // 交换两个线程

    void swap();

    namespace this_thread

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    // 将当前线程挂起,等待下次调度,让其他线程运行

    void yield();

    // 返回当前线程的id

    std::thread::id get_id()

    // 阻塞当前线程的执行一定时间,时间由sleep_duration指定

    template<class Rep, class Period>

    void sleep_for(const std::chrono::duration<Rep, Period>& sleep_duration);

    // 阻塞当前线程直到某个时间点,时间点由sleep_time指定。

    template< class Clock, class Duration >

    void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
    文章目录
    1. 1. std::thread
      1. 1.0.1. Hello World
      2. 1.0.2. 成员函数
  • 2. namespace this_thread