理解多线程(一)

引入并发概念

单道操作系统

其实在单道系统时期,是没有并发的,cpu更是没有多核。程序员输入数据,计算机处理,计算机输出结果,每次一个程序执行完才能执行其他程序。但不久聪明的程序员们发现这样极大的浪费计算机资源。比如,在执行一个算术问题时候,程序员输入数据是很慢的(scanf())。而这几秒钟cpu却阻塞在这里等待IO完成。

多道操作系统

所以,为了克服单道系统的各种问题。研究出了多道操作系统,让计算机可以同时运行多个程序(宏观上)。让程序在等待IO时候,而CPU在执行其他程序,提高效率,节约资源。计算机系统采用分时复用的方式。将进程(运行的程序)贴上时间片的标签。可以理解为A进程时间片1s,B进程时间片2s,时间片代表进程能用于cpu的时间。A进程装载进cpu后1s后,A进程退出,让B进程装载进cpu运行2s,如此循环。所以在人眼中,敲代码和听音乐是同时进行的。在微观上,其实是进程在分时使用CPU,只是时间片太短,以至于使用者是感觉不到的

并发

所以参考度娘,并发有如下定义:
并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。

引入并行概念

并发只是宏观上的并行(进程同时运行),随着计算机的发展,CPU不再是以前的单核 ,渐渐有了2核心,4核心,8核心。这样A进程可以在1核心上运行的同时,B进程在2核心上运行。多核计算机上实现了真正的宏观上微观上的并行。

引入并发并行的实现方式

实现并行和并发一般分为两种,两者各有优势,可以根据实际需求考虑。
1,多进程实现
2,多线程实现
线程就是一个轻量级的进程,就如操作系统里这么说" 进程是资源分配的最小单位,线程是CPU调度的最小单位”。线程不拥有资源,线程公用一个进程的资源,这也奠定了线程占用内存小,共享数据简单但线程的安全是一个大问题。

实现方式

多进程的实现,进程的安全由操作系统进行管理,程序员不用太关心进程的安全,而更多在于多个进程的通信,多进程的通信方式有管道,共享内存,消息
多线程的实现,由于不占用资源,共享资源,所以数据的通信是很简单的。而共享资源的资源是不安全的。由于操作系统不会提供线程的安全机制,所以需要程序员来维护资源的安全。这也是多线程的研究方向。

优缺点

内存方面: 多进程占用大,多线程占用小
CPU方面:多进程利用率低,多线程利用率高
数据通信:多进程数据通信复杂,多线程数据通信简单
数据安全:多进程容易,多线程困难
总体速度:多进程慢,多线程快
安全方面:多进程更安全,多线程不安全。


看看标题,我主要讲多线程方面。下面是一个线程安全的栗子。


线程的安全问题

c++自11版本开始支持线程,线程的操作放在 #include 头文件中声明,因此使用 线程时需要包含 #include 头文件。
创建一个线程:
std::thread t1(fun,参数);
对象方法detach是将主进程和线程分离运行;
如下,我想用线程打印出一段数据。
在这里插入图片描述
但结果是出乎意料的,线程并没有根据我预想的打印出来。线程是多个的,甚至CPU也可以多核,但控制台只有一个。线程操作控制台式无序的。你不能知道是A线程先运行还是B线程先运行。甚至线程内部的操作也是不同步的,如上第一行。线程一还没有打印换行,控制台就被线程2抢去打印了。所以最后引入了线程间是需要同步的

栗子2

再举个栗子,如下,fun1先sleep一秒钟,然后数据自加操作。fun2先sleep一秒钟,然后数据自减操作。两种线程都运行100次。自加100次,自减100次,理论上最后结果应该是0

在这里插入图片描述
结果也是如此,与理论不符。这涉及底层原理。反汇编看看源码。

	//do something
	data++;
00FC80F3  mov         eax,dword ptr [data]  
00FC80F6  mov         ecx,dword ptr [eax]  
00FC80F8  add         ecx,1  
00FC80FB  mov         edx,dword ptr [data]  
00FC80FE  mov         dword ptr [edx],ecx  

自加操作,内存中的data先会写入寄存器中,由寄存器进行自加操作后,最后会将寄存器结果写入data内存。
这样问题就来了。假如data为0,自加操作进行到add ecx,1 ,寄存器存储值为1,还没写进内存,时间片到期了。自减线程开始操作。而自减操作读取的仍然是data值为0,自减得到-1,写入内存,时间片到期。刚才的自加线程继续运行。将寄存器中的1写入data内存。最后一次自加操作和自减操作得到了结果1。所以引入了线程还需要考虑访问数据的安全问题

猜你喜欢

转载自blog.csdn.net/qq_35651984/article/details/84894221