C++11引入了std::thread,用于多线程编程。
本文从实际应用的角度,分享一些常见的std::thread基础用法。
创建线程 我们从一个最简单的例子入手:
1 2 3 4 5 6 7 8 9 10 11 #include  <iostream>  #include  <thread>  void  hello ()     std ::cout  << "Hello std::thread!"  << std ::endl ; } int  main ()     std ::thread t (hello)  ;     t.join(); } 
上面的例子中我们做了四件事:
引入了头文件#include <thread> 
编写了一个独立的函数hello,它没有参数,没有返回值 
创建了std::thread对象,它的参数是函数指针hello 
调用了std::thread的join成员函数,join会等待线程直到执行完成,我们在稍后会介绍它 
 
构造一个std::thread对象 从上面最简单的例子可以看出,我们通常这样构造一个线程对象
1 2 void  do_some_work () std ::thread my_thread (do_some_work)  ;
其实,可以使用任何可Callable的类型,来构造std::thread,比如函数对象:
1 2 3 4 5 6 7 8 9 10 11 class  background_task { public :    void  operator () ()  const        {        do_something();         do_something_else();      } }; background_task f; std ::thread my_thread (f)  ;
使用Lambda 或者我们可以使用匿名函数,就像下面这样:
1 2 3 4 std ::thread my_thread ([](      do_something();     do_something_else(); }); 
join & detach 在上面最简单的例子中,我们知道了,使用join()可以等待线程直到执行完成。
1 2 3 4 5 6 7 std ::thread t1 ([]() {      for  (int  i = 0 ; i < 10 ; i++) {         std ::cout  << "in t1: "  << i + 1  << std ::endl ;     } }) t1.join(); std ::cout  << "t1 done."  << std ::endl ;
它的输出是
1 2 3 4 5 6 7 8 9 10 11 in t1: 1 in t1: 2 in t1: 3 in t1: 4 in t1: 5 in t1: 6 in t1: 7 in t1: 8 in t1: 9 in t1: 10 t1 done. 
直到t1线程执行完成后,才打印出t1 done。
与join对应的,detach用来在后台运行线程。 
我们尝试把上面例子的join改成detach
1 2 3 4 5 6 7 8 std ::thread t1 ([]() {      for  (int  i = 0 ; i < 10 ; i++) {         std ::cout  << "in t1: "  << i + 1  << std ::endl ;     } }) t1.detach(); std ::cout  << "t1 done."  << std ::endl ;std ::this_thread::sleep_for(std ::chrono::seconds(1 ));
打印的结果变成了
1 2 3 4 5 6 7 8 9 10 11 t1 done. in t1: 1 in t1: 2 in t1: 3 in t1: 4 in t1: 5 in t1: 6 in t1: 7 in t1: 8 in t1: 9 in t1: 10 
传参 在std::thread构造函数中添加额外的参数就好了。
1 2 3 4 5 void  foo (int  m, int  n)     std ::cout  << m << n << std ::endl ; } std ::thread t (foo, 5 , 4 )  ;
注意,给类的成员函数传参,要加上对象指针。就像下面这样:
1 2 3 4 5 6 7 8 struct  Foo  {    void  bar (int  n)           std ::cout  << n << std ::endl ;     } }; Foo foo; std ::thread t (&Foo::bar, &foo, 5 )  ;t.join(); 
返回值 我们如何从线程中拿到返回值呢?
Lambda + 引用捕获 1 2 3 4 5 6 int  r = 0 ;std ::thread t ([&r]() {      r = 10 ; }) t.join(); std ::cout  << r << std ::endl ; 
使用std::promise和std::future std::promise和std::future提供了一种可以从异步方法中拿到执行结果的机制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include  <iostream>  #include  <thread>  #include  <future>  void  foo (int  m, int  n, std ::promise<int >&& p)      p.set_value(m * n); } int  main ()     std ::promise<int > p;     auto  f = p.get_future();     std ::thread t (&foo, 5 , 6 , std ::move(p))  ;     t.join();     int  i = f.get();      std ::cout  << i << std ::endl ;  } 
使用std::async 使用std::async可以简化上面例子的写法,它异步的执行一个函数,返回std::future类型的结果。
上面的例子可以写成:
1 2 3 4 5 6 7 8 9 10 11 12 #include  <iostream>  #include  <thread>  #include  <future>  int  foo (int  m, int  n)      return  m * n; } int  main ()     auto  f = std ::async(foo, 5 , 6 );     std ::cout  << f.get() << std ::endl ; } 
在线程之间共享数据 假设我们用一个单例来管理这些共享数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class  Singleton  {public :    static  Singleton& instance ()       {        static  Singleton s;         return  s;     }     void  add (int  n)       {        v_.emplace_back(n);     }     int  count ()  const       {        return  v_.size();     } private :    Singleton() {} public :    Singleton(Singleton const &) = delete ;     void  operator =(Singleton const &) = delete ; private :    std ::vector <int > v_; }; int  main ()     std ::vector <std ::thread> threads;     for  (int  i = 0 ; i < 10 ; ++i) {         threads.emplace_back(std ::thread([](int  i) {             Singleton::instance().add(i);         }, i));     }     for  (auto & t : threads) {         t.join();     }     std ::cout  << Singleton::instance().count() << std ::endl ; } 
我们同时run了10个线程,每个线程往池子里丢一个数据。理论上应该是10个,但实际上可能并非如此。
加锁 我们对上面的Singleton稍作修改,加一个互斥量,并对往池子丢数据的地方加锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class  Singleton  {public :    static  Singleton& instance ()       {        static  Singleton s;         return  s;     }     void  add (int  n)       {        std ::lock_guard<std ::mutex> guard(mutex_);         v_.emplace_back(n);     }     int  count ()  const       {        return  v_.size();     } private :    Singleton() {} public :    Singleton(Singleton const &) = delete ;     void  operator =(Singleton const &) = delete ; private :    std ::mutex mutex_;     std ::vector <int > v_; }; 
这样,我们就能得到期望的结果了。
终止线程 有可能我们在关闭主程序时,我们的子线程还没结束。我们需要正常的结束子线程。
加个标志 1 2 3 4 5 6 7 8 9 10 std ::atomic_bool  stop_flag = false ;void  foo ()     while  (true ) {         if  (stop_flag) {             return ;         }              } } 
然后在主线程中改变这个标志
1 2 3 4 std ::thread t (foo)  ;std ::this_thread::sleep_for(std ::chrono::milliseconds(100 )); stop_flag = true ; t.join(); 
其他 同时跑几个线程合适? 推荐使用std::thread::hardware_concurrency(), 在多核架构的运行环境上,这个返回值一般对应核的颗数。
获取当前线程的标识 使用std::this_thread::get_id()
std::async支持两种策略 默认使用std::launch::async , 惰性调用时使用std::launch::deferred 
文章中没提到的 本文介绍的只是基础的多线程使用。有很多主题都没有介绍,比如如何在多个线程中进行同步,原子操作等等。
Reference