漫话 C++11 之 thread

熟悉C++的人知道,在经过了13年的漫长等待后,C++标准委员会终于在2011年通过了C++史上的第二个标准。

C++0x VS C++11,傻傻分不清楚

从一开始到现在,C++大概经历了以下几个比较重要的阶段:

  • 1998: ISO官方发布C++标准,俗称C++98,这是第一个C++的官方正式版本
  • 2003: TC1(Technical Corrigendum 1)发布,俗称C++03, 这个版本可以当成是C++98的一个bugfix版本
  • 2005:TR1(Technical Report 1)发布,TR1是一个新增加的库,增加了大约14种新的组件到C++标准中
  • 2008:新C++标准(C++0x)草案发布,这个主要是以TR1的基础上进行了扩充
  • 2011:C++0x标准通过

随着各种动态解释型语言的诞生,C++程序员也越来越感觉到了C++语言本身的一些局限性。以C++之父 Bjarne Stroustrup(没错,就是下面这个仙风道骨的老头) 为代表的先贤们,也一直在不遗余力的努力着,让C++更加强大。

于是他们商量着:

要么大伙每5年来一次华山论剑?

好!果真,后来的C++大会基本上是5年一次,比如 2003年,2008年。

1998年C++标准发布后,原计划于2003年之后的几年推出新的标准,但由于没有确定具体哪一年发布,就用 0x原来表示的是04-09年中的某一年。

等到了2009年,新的C++0x标准却一致拖着没通过,直到2011年才通过,名称还保持叫C++0x

x表示是16进制的字符0-f,所以11也是x了。

所以说,C++0xC++11,这俩就是一回事,只不过前者是个草案,而后者是正式通过的标准。

thread

以前,由于 C++98中没有线程、锁、条件变量等这些与多线程相关的特性支持,如果需要写多线程程序,都要借助于不同平台上各自提供的 api,导致程序的跨平台移植性较差,经常要用一大堆的 #ifdef WIN32类似的宏来区分不同的平台,这样写代码的姿势很难看。

现在好了,使用 C++11 可以编写跨平台的多线程程序了,而且相比原来的 pthread写法,代码更简洁优美。

今天从 thread 开始,介绍下 C++11 带来的一系列新特性。

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
37
38
39
40
#include <iostream>
#include <string>
#include <thread>

using namespace std;

class Foo
{
public:
void bar (int id, string& name) {
name += "+1s";
cout << "id=" << id << ",name=" << name << endl;
}
};

void func ()
{
cout << "thread func" << endl;
}

int main ()
{
Foo foo;
int id = 1;
string name("elder");

thread th_1 (func); //thread func
th_1.join ();

thread th_2 (&Foo::bar, foo, id, ref(name));//id=1,name=elder+1s
th_2.join ();

thread th_3 (bind (&Foo::bar, foo, id, name));//id=1,name=elder+1s+1s
th_3.join ();

thread th_4 ([&]{ foo.bar (id, name); });//id=1,name=elder+1s+1s
th_4.join ();

return 0;
}

上面是 thread 的四种构造方式:

(1) 最简单,最常用的方式,直接传入一个普通函数。

(2) 变参模版方式,如果函数为类的成员函数,第一个参数是对象本身,后面的参数按函数调用时的顺序传入。注意,引用传递参数的话实参用 ref(arg) 包装。

(3) bind 方式,传入一个 std::function对象,同样地,如果是类成员变量的话,第一个参数是对象本身。

(4) lambda 方式,是比较常用的方式(先留个坑,以后再细述)。

thread 对象

  • 不可被拷贝构造
  • 可以 `move` 方式构造,thread th(std::move(x)); 之后,新构造的 th 拥有原先 x 的执行对象,x 不再代表执行对象。
  • 可被 joinablethread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detach

thread 方法

1.operator =

如果当前对象不可 joinable,将获取右值的执行对象,同时,右值将不再代表执行对象;
如果当前对象可被 joinable,则 terminate() 报错。

1
2
3
thread th;
th = thread(func); //√
th = th_1; //x

2.get_id()

如果当前对象不可 joinable,返回默认构造线程的id(0x0)
如果当前对象可被 joinable,则返回当前线程的 id

3.joinable()

检测线程是否 joinable。当且仅当 thread 代表某个执行对象时,它才是 joinable的。下列条件下,属于 不可 joinable

a. 默认构造函数构造出来。对 thread th;而言,th 不代表任何 thread 执行对象。
b. 被作为参数给 std::move 调用。
c. 已经调用了 joindetach 函数。

4.swap(x)

交换当前线程和 x 的状态、执行对象等。

参考

彦祖老师 wechat