cpp11多线程1-thread

std::thread

在C++11之前,C++语言层面是不支持多线程的,想利用C++实现并发程序,借助操作系统的API实现跨平台的并发程序存在着诸多不便,当C++11在语言层面支持多线程后,编写跨平台的多线程代码就方便了许多。

C++11提供的std::thread在开发多线程方面带来了便捷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <thread>

void threadfunc()
{
std::cout << "thread func" << std::endl;
}
​​
int main()
{
std::thread t1(threadfunc);
t1.join(); //等待threadfunc运行结束

return 0;
}

首先定义线程对象t1,线程函数threadfunc运行在线程对象t1中,当线程创建成功并执行线程函数后,一定要保证线程函数运行结束才能退出,这里调用了join()函数阻塞线程,直到threadfunc()运行结束,回收对应创建线程的资源。如果不阻塞线程,就不能保证线程对象t1在threadfunc()运行期间有效,下面不调用join()阻塞线程。

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

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


int main()
{
std::thread t1(threadfunc);
//t1.join(); //等待threadfunc运行结束

return 0;
}

````
在运行时引起了程序崩溃。
![error1.jpg](error1.jpg)

除了调用join()阻塞线程,保证线程对象在线程函数运行期间的有效性,还可以通过线程分离的手段实现,调用detach()函数使得线程对象与线程函数分离,这样,在线程函数运行期间,线程对象与线程函数就没有联系了,此时的线程是作为后台线程去执行,detach()后就无法再和线程发生联系,也不能通过join()来等待线程执行完毕,线程何时执行完无法控制,它的资源会被init进程回收,所以,通常不采用detach()方法。

#include
#include

void threadfunc()
{
std::cout << “ detach thread func” << std::endl;
}

int main()
{
std::thread t1(threadfunc);
t1.detach(); //线程分离

return 0;
}

1
2

这里调用detach()实现线程分离,但是运行后,主线程退出的时候threadfunc()还没有输出“detach thread func”,threadfunc()什么时候运行结束也无法确定,为了看到所创建的线程运行结果,在主线程等待一下再退出。

#include
#include
#include //时间

void threadfunc()
{
std::cout << “detach thread func” << std::endl;
}

int main()
{
std::thread t1(threadfunc);
t1.detach();
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1000毫秒
break;
}

return 0;

}

1
此时运行结果:

detach thread func

1
通过std::thread创建的线程是不可以复制的,但是可以移动。

#include
#include
#include

void threadfunc()
{
std::cout << “move thread func” << std::endl;
}

int main()
{
std::thread t1(threadfunc);
std::thread t2(std::move(t1));

t2.join();
while (true)
{
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1000毫秒
    break;
}

return 0;

}

1
输出结果:

move thread func

1
2
3
4
5
6
7

移动后t1就不代表任何线程了,t2对象代表着线程threadfunc()。

## 其他创建线程方式

### 通过std::bind来创建线程函数。

#include
#include
#include //时间
#include //std::bind

class A {
public:
void threadfunc()
{
std::cout << “bind thread func” << std::endl;
}
};


int main()
{
A a;
std::thread t1(std::bind(&A::threadfunc,&a));
t1.join();
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1000毫秒
break;
}

return 0;

}


创建一个类A,然后再main函数中将类A中的成员函数绑定到线程对象t1上,运行结果:
1
bind thread func
### 仿函数对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <thread>
#include <iostream>

using namespace std;
class Test{
public:
void operator()(){
cout << "子线程开始执行!" << endl;
//do something
cout << "子线程执行完毕!" << endl;
}

};
int main()
{
// thread t(Test()); 这种写法会编译器会认为是一个函数声明,这个函数带有一个参数(函数指针指向没有参数并返回Test对象的函数)
thread t((Test())); //可以使用加小括号,或者使用一只初始化,或者传入命名变量
t.join(); //主线程等待子线程
return 0;
}
### lambda表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <thread>
#include <iostream>

using namespace std;

int main()
{
thread t(
[] () {
cout << "子线程开始执行!" << endl;
//do something
cout << "子线程执行完毕!" << endl;
}
);
t.join(); //主线程等待子线程
return 0;
}
### 非静态成员函数,静态成员函数 以下的下写法中是将对象拷贝了一份副本来创建线程。当我们在进行共享数据的管理时,有时候需要传入对象的指针或者地址。而静态成员函数,不需要传入对象,只需要传入类函数地址。根据C++对象模型,这很好理解因为编译器对非静态成员函数的处理需要this指针,而对静态成员函数的处理不需要。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <thread>
#include <iostream>

using namespace std;
class Test{
public:
void test(){
cout << "子线程开始执行!" << endl;
// do somesthing
cout << "子线程执行完毕!" << endl;
}
};

int main()
{
Test obj;
thread t(&Test::test, obj); //注意写法,传入成员函数地址,还需要传入对象
t.join(); //主线程等待子线程
return 0;
}
## 获取线程id   线程标识类型是 std::thread::id 类型。有两种获取id的方法。   方法一:可以通过调用 std::thread 对象的成员函数 get_id() 来获取id。   方法二:在当前线程中使用std::this_thread::get_id()来获取线程id。
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
#include <iostream>
#include <thread>
#include <chrono>
#include <functional>

class A {
public:
void threadfunc()
{
std::cout << "bind thread func" << std::endl;
}
};


int main()
{
A a;
std::thread t1(std::bind(&A::threadfunc,&a));

std::cout << "main thread ID is : " << std::this_thread::get_id() << std::endl;
std::cout << "t1 thread ID is : " << t1.get_id() << std::endl;

t1.join();
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1000毫秒
break;
}

return 0;
}
std::this_thread::get_id()获取的是当前线程的ID,t1.get_id()获取的是所创建的t1对象中运行的线程ID,对应的ID分别为:
1
2
3
main thread ID is : 11932
t1 thread ID is : 12076
bind thread func
虽然get_id()可以获取线程的ID,但是其返回类型是thread::id,通过std::cout可以输出线程ID,但是这样使用似乎不太方面,要是能转换为整形就好了。其实可以将得到的线程ID写入到ostreamstring流中,转换成string类型,再转换成整形。
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
#include <iostream>
#include <thread>
#include <chrono>
#include <functional>
#include <sstream>

class A {
public:
void threadfunc()
{
std::cout << "bind thread func" << std::endl;
}
};


int main()
{
A a;
std::thread t1(std::bind(&A::threadfunc, &a));

std::ostringstream os1;
os1 << t1.get_id() << std::endl;
std::string strID = os1.str(); //转换成string类型
int threadID = atoi(strID.c_str()); //转换成int类型
std::cout << "t1 thread ID is : " << threadID << std::endl;

t1.join();
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1000毫秒
break;
}

return 0;
}
输出结果:
1
2
3
t1 thread ID is : 6956
bind thread func

参考: https://zhuanlan.zhihu.com/p/157171731 https://www.cnblogs.com/chen-cs/p/13054027.html