C++多线程__新__研二下

//F9加断点

一、并发、进程、线程的基本概念

1、并发:两个或者更多的任务(独立的活动)同时进行,一个程序同时执行多个独立的任务。以往计算机只是单核CPU,某一个时刻只能执行一个任务,由操作系统调度,每秒钟多次进行任务切换,不是真正的并发。现在计算机多是多核CPU,能够真正的并行执行多个任务(硬件并发)
2、可执行程序:磁盘上的文件,windows下扩展名为.exe的文件
3、进程:一个可执行程序运行起来就叫一个进程运行起来了,或者说进程就是运行起来了的可执行程序
4、线程:每个进程都有一个主线程(自动创建,一般是main函数),实际上运行程序的时候,实际上是该进程的主线程在运行,线程就是执行代码的一条道路,除了主线程之外,可以自己写代码创建其他线程,每创建一个新线程就可以多干一个不同的事,但是线程并不是越多越好(子线程最多不可超过300个),每个线程都需要一个独立的堆栈空间,线程之间的切换是要保存中间数据的,会耗费本该是程序运行的时间
总结线程:
a 线程是用来执行代码的
b 把线程理解为一个新的通路
c 一个进程自动包含一个主线程,主线程随着进程的自动启动和结束
d 多线程程序可以同时做多个事

二、并发的实现方法

a)多个进程实现并发
b)在单独的一个进程中,创建多个线程实现并发
1、多进程并发
world ie浏览器 爱奇艺等软件同时运行
进程之间的通信(同一台电脑)方法:管道、文件、消息队列等
进程之间的通信(不同电脑)方法:socket通信技术
2、多线程并发
每个线程都有自己独立的运行路径,且共享地址空间(共享内存)
全局变量、指针、引用都可以在不同线程之间传递,共享内存也胡存在一些问题,如数据一致性问题,例如线程1和线程2同时对一个变量进行操作时候,就会出现意外。
总结:线程如下优点
1)线程启动速度比进程快
2)系统资源开销更少
缺点:存在数据一致性问题
三、C++11新标准线程库(可以跨平台widows和Linux)
C++11增加了对多线程的支持,意味着可移植性

第二节

一、线程开始和结束

程序运行起来,生成一个进程,该进程所在的主进程自动运行
自己创建的线程也需要从一个函数开始执行,如果这个函数执行完毕该线程也运行结束
一般情况下,如果主线程执行完毕,子线程还没有结束,那么整个子线程会被系统强行终止
所以一般情况下,如果想保持子线程的运行状态,就要让主线程一直保持运行

1、thread是标准库中的一个类
2、join阻塞主线程,让主线程等待子线程执行完毕,
实例:

 1 #include <iostream>
 2 #include <thread> //尖括号表示系统头文件
 3 
 4 using namespace std;
 5 
 6 void myprint()
 7 {
 8 cout<<"我的线程开始执行"<<endl;
 9 //可以做一些其他的事情
10 cout<<"我的线程执行结束"<<endl;
11 }
12 
13 int main()
14 {
15 thread mytobj(myprint); //创建了线程,该线程执行起点为myprint(),该线程开始执行
16 
17 //主线程可以做一些其他的事情,其中这些事情和myorint()函数是同时执行的
18 
19 mytobj.join(); //主线程阻塞到这里,子线程继续执行,主线程等待子线程执行完毕,当子线程执行完毕,主线程就继续向下执行
20 
21 
22 cout<<"HelloWorld"<<endl;
23 
24 return 0;
25 }
View Code

//如果不加join()此时这个代码有两条线同时在跑,打印"我的线程开始执行"和打印"HelloWorld"是通过不同线路来打印的
3、detach()
传统主线程要等待子线程执行完毕再推出,但是主线程也可以和子线程分离,即主线程可以提前结束,以提高程序运行效率
一旦detach()之后,与这个主线程关联的线程对象就会失去了与主线程join的资格
此时子线程在后台运行,这个子线程被C++运行时库接管

 1 #include <iostream>
 2 #include <thread> //尖括号表示系统头文件
 3 
 4 using namespace std;
 5 
 6 void myprint()
 7 {
 8 cout<<"我的线程开始执行"<<endl;
 9 //可以做一些其他的事情
10 cout<<"我的线程执行结束1"<<endl;
11 cout<<"我的线程执行结束2"<<endl;
12 cout<<"我的线程执行结束3"<<endl;
13 cout<<"我的线程执行结束4"<<endl;
14 cout<<"我的线程执行结束5"<<endl;
15 cout<<"我的线程执行结束6"<<endl;
16 cout<<"我的线程执行结束7"<<endl;
17 cout<<"我的线程执行结束8"<<endl;
18 cout<<"我的线程执行结束9"<<endl;
19 cout<<"我的线程执行结束10"<<endl;
20 cout<<"我的线程执行结束11"<<endl;
21 }
22 
23 int main()
24 {
25 thread mytobj(myprint); //创建了线程,该线程执行起点为myprint(),该线程开始执行
26 
27 //主线程可以做一些其他的事情,其中这些事情和myorint()函数是同时执行的
28 
29 mytobj.detach(); //主线程阻塞到这里,子线程继续执行,主线程等待子线程执行完毕,当子线程执行完毕,主线程就继续向下执行
30 
31 
32 cout<<"HelloWorld1"<<endl;
33 cout<<"HelloWorld2"<<endl;
34 cout<<"HelloWorld3"<<endl;
35 cout<<"HelloWorld4"<<endl;
36 cout<<"HelloWorld5"<<endl;
37 cout<<"HelloWorld6"<<endl;
38 cout<<"HelloWorld7"<<endl;
39 
40 return 0;
41 }
View Code

打印可能是:

 1 我的线程开始执行
 2 我的线程执行结束1
 3 我的线程执行结束2
 4 HelloWorld1
 5 HelloWorld2
 6 HelloWorld3
 7 HelloWorld4
 8 我的线程执行结束8
 9 我的线程执行结束9
10 我的线程执行结束3
11 HelloWorld5
12 HelloWorld6
13 HelloWorld7
14 执行完毕
View Code

//此时子线程和主线程同时执行,但是这样可能会存在主线内的代码执行完毕,但是子线程中的代码还没有执行完毕的现象

4、detachable():判断是否可以使用join或detach,返回true表示可以join,否则表示不可join
5、其他创建线程的方法
类对象也为可调用对象

 1 例如:
 2 #include <iostream>
 3 #include <thread> //尖括号表示系统头文件
 4 
 5 using namespace std;
 6 
 7 class TA
 8 {
 9 public:
10 void operator()(); //不能带参数
11 {
12 cout<<"子线程开始1"<<endl;
13 cout<<"子线程开始2"<<endl;
14 cout<<"子线程开始3"<<endl;
15 cout<<"子线程开始4"<<endl;    
16 }
17 
18 };
19 
20 void myprint()
21 {
22 cout<<"我的线程开始执行"<<endl;
23 //可以做一些其他的事情
24 cout<<"我的线程执行结束1"<<endl;
25 cout<<"我的线程执行结束2"<<endl;
26 cout<<"我的线程执行结束3"<<endl;
27 cout<<"我的线程执行结束4"<<endl;
28 cout<<"我的线程执行结束5"<<endl;
29 cout<<"我的线程执行结束6"<<endl;
30 cout<<"我的线程执行结束7"<<endl;
31 cout<<"我的线程执行结束8"<<endl;
32 cout<<"我的线程执行结束9"<<endl;
33 cout<<"我的线程执行结束10"<<endl;
34 cout<<"我的线程执行结束11"<<endl;
35 }
36 
37 int main()
38 {
39 TA ta;
40 thread mytobj3(ta); //ta为可调用对象 类对象也为可调用对象
41 mytobj3.join(); //等待子线程执行结束,也可以用detach()
42 
43 //主线程可以做一些其他的事情,其中这些事情和myorint()函数是同时执行的
44 
45 cout<<"HelloWorld1"<<endl;
46 
47 return 0;
48 }
View Code

6、类中的私有数据含有引用或者是指针时候会出现意外

如下代码:

 1 #include <iostream>
 2 #include <thread>    //尖括号表示系统头文件
 3 
 4 using namespace std;
 5 
 6 class TA
 7 {
 8     int & m_i;
 9     public:
10     TA(int & i):m_i(i);
11     void operator()()  //不能带参数
12     {
13         cout<<"m_i的值为"<<m_i<<endl;
14         cout<<"m_i的值为"<<m_i<<endl;
15         cout<<"m_i的值为"<<m_i<<endl;
16         cout<<"m_i的值为"<<m_i<<endl;
17         cout<<"m_i的值为"<<m_i<<endl;
18         cout<<"m_i的值为"<<m_i<<endl;
19         cout<<"m_i的值为"<<m_i<<endl;
20         
21     }
22     
23 };
24 
25 void myprint()
26 {
27     cout<<"我的线程开始执行"<<endl;
28     //可以做一些其他的事情
29     cout<<"我的线程执行结束1"<<endl;
30     cout<<"我的线程执行结束2"<<endl;
31     cout<<"我的线程执行结束3"<<endl;
32     cout<<"我的线程执行结束4"<<endl;
33     cout<<"我的线程执行结束5"<<endl;
34     cout<<"我的线程执行结束6"<<endl;
35     cout<<"我的线程执行结束7"<<endl;
36     cout<<"我的线程执行结束8"<<endl;
37     cout<<"我的线程执行结束9"<<endl;
38     cout<<"我的线程执行结束10"<<endl;
39     cout<<"我的线程执行结束11"<<endl;
40 }
41 
42 int main()
43 { 
44     int myi=6;
45     TA ta(myi);
46     thread mytobj3(ta);  
47     mytobj3.detach();     
48     
49     //主线程可以做一些其他的事情,其中这些事情和myorint()函数是同时执行的
50     
51     cout<<"HelloWorld1"<<endl;
52     
53     return 0;
54 }
View Code

主线程先执行完,子线程还在执行,myi是主线程的变量,主线程执行完myi就会被销毁,子线程再去使用myi的时候就会出错
或者将类TA中的公有数据改成不是引用也是可以的,这样就会使用主线程中myi的拷贝,这样就没有问题了

主线程结束后,ta也会被销毁,但是ta不在没有关系,ta是会被复制到了子线程中去的(所以在类中要有复制构造函数),所以不会因此出错,如下例程:
只要类中私有数据没有引用、指针,使用detach()就没有问题

 1 #include <iostream>
 2 #include <thread>    //尖括号表示系统头文件
 3 
 4 using namespace std;
 5 
 6 class TA
 7 {
 8 private:
 9     int  m_i;
10 public:
11     TA(int & i) :m_i(i)
12     {
13         cout << "构造函数被执行" << endl;
14     }
15     TA(const TA & ta) :m_i(ta.m_i)
16     {
17         cout << "复制构造函数被执行" << endl;
18     }
19     ~TA()
20     {
21         cout << "析构函数被执行" << endl;
22     }
23 
24     void operator()()  //不能带参数
25     {
26         cout << "m_i1的值为" << m_i << endl;
27         cout << "m_i2的值为" << m_i << endl;
28         cout << "m_i3的值为" << m_i << endl;
29         cout << "m_i4的值为" << m_i << endl;
30         cout << "m_i5的值为" << m_i << endl;
31         cout << "m_i6的值为" << m_i << endl;
32         cout << "m_i7的值为" << m_i << endl;
33     }
34 
35 };
36 
37 int main()
38 {
39     int myi = 6;
40     TA ta(myi);
41     thread mytobj3(ta);
42     mytobj3.join();
43 
44     //主线程可以做一些其他的事情,其中这些事情和myorint()函数是同时执行的
45 
46     cout << "HelloWorld1" << endl;
47 
48     system("pause");
49     return 0;
50 }
View Code

执行结果:

 但是主线程中的类对象ta执行析构函数没有显示不知道为啥。。注:以上方法也可以改为detach()

7、用Lamda表达式创建子线程(此处只写出了主函数)

 1 int main()
 2 {
 3     auto mylambdathread = [] 
 4     {
 5         cout<<"我的子线程开始执行"<<endl;
 6     }
 7     thread mytobj(mylambdathread);
 8     mytobj.join();
 9     
10     return 0;
11 }
View Code

猜你喜欢

转载自www.cnblogs.com/YiYA-blog/p/12466003.html
今日推荐