一、多线程是什么?
1.在说起多线程前,要先来说说进程,那么进程又是什么呢?
进程进程顾名思义,就是运行中的程序,这个说法不够精确。在操作系统中,一旦一个程序被运行起来,它就会被加载到内存中,操作系统就会为它创建一个进程控制块来将这个程序描述起来。至此以后运行的信息都会被进程控制块记录起来(PCB),因此进程控制块就是进程就是PCB,在Linux下进程可以看作是task_struct结构体。
2.说完进程,那么线程是什么?二者又有什么关系?
在Linux下线程是用PCB来模拟的,因此Linux下的线程就是轻量级进程。Linux下没有真正的线程。那么如果说此时线程变成了轻量级进程,那么进程又是什么?这时进程就变成了线程组,进程里面不仅包含了每个线程的各自信息还有它们的共享信息。一个进程中至少包含了一个或多个线程。
只有在程序运行时会给进程分配资源,所以进程或者线程组是资源分配的基本单位。而线程中操作系统不会为它单独分配自己所需要的所有资源。而是在为进程分配的资源中来划分一块区域为己独有,并且此进程下的所有线程共用一个虚拟地址空间,因此共享进程的代码段和数据段。Linux下pcb成为了线程所有线程是cpu调度的基本单位。
3.那么多线程共享哪些数据,又有哪些独特的数据呢?
1.共享的数据有:
1.文件描述符。2.每种信号的处理方式(SIG_IGN, SIG_DFL或者自定义的信号处理函数)。3.当前工作目录。4.用户ID和组ID。
2.独有数据:
虽然共享了这么多数据,但是每个线程有自己的pcb,因此线程可以同时运行,并且不会造成调用栈混乱。
1.栈。2.寄存器(文件描述符)。3.信号屏蔽字。4.errno错误信息。5.线程标识符。
4.多线程的优缺点:
主要从两方面来分析:IO密集和CPU密集来说
优点:由于多个线程共享虚拟地址空间所以:
(1)线程间通信简单
(2)线程的创建/销毁成本更低
(3)线程的调度成本更低
(4)线程的执行粒度更细
缺点:
(1)线程缺乏访问控制----exit退出将是整个进程
(2)健壮性较低---线程的某些错误会导致整个进程的退出或者失败。
二、来说说多线程的控制
由于Linux下没有真正的线程,所以在操作系统中没有提供直接创建线程的系统调用接口。这将使得我们实现一个线程的创建将变得异常艰难。所以对此那些巨佬就不服气了,他们实现了一套线程控制接口---封装了线程库来给我们使用。所以在此创建的线程是用户态的线程,在调用时,内核中对应有轻量级进程实现程序的调度运行。
线程创建---》线程终止---》线程等待---》线程分离---》线程安全。
1.线程创建:pthread_create
在这个函数中的参数thread是用来保存返回的线程ID,attr是线程属性,一般置NULL,star_routine是线程入口函数,arg是给线程传递的参数。
代码实现:
运行结果:
在代码中tid就是创建的线程地址空间的首地址。
在这里PID就是线程组ID也就是进程PID。进程的PID就是程序运行起来第一个被CPU执行的线程的PID。LWP是每个线程自己的PID。
2.线程终止:
在main中renturn将退出进程,而在函数中即线程入口函数中return就是退出线程。
退出调用线程函数:pthread_exit 退出调用线程,pthread_cancel 取消指定线程。
代码实现线程终止:
运行结果:
子进程被终止。那么自己可不可以取消自己呢?
在下面我取消主线程运行显示:
虽然这样可以看出主线程被终止,但这属于非法操作,但是出现了僵尸线程。这就引出来下一个线程问题:
3.线程等待:线程退出也会形成僵尸线程
线程在创建出来以后,默认带有一个属性即joinable属性,当线程处于这个属性时,线程必须被等待退出,才能将资源释放。否则它不会自动回收资源从而造成资源泄露。
pthread_join函数
代码显示:
运行结果:
此时就不会产生僵尸线程,因为接受线程的退出返回值。资源被释放。
4.线程分离:
既然有时我们没有必要接受线程的退出返回值。就需要分离线程。分离线程只需将它的joinable属性修改为datach属性,处于这个属性的线程,退出后将自动回收资源。并且这个属性的线程不能被等待,否则报错。
pthread_detach(tid):
thread:线程的pid
代码实现:
运行结果:
此时创建的线程已经被分离,pthread_join将获取不到线程的退出返回值所以乱码。
线程分离可以在任何线程时间分离,包括分离自己。
5.线程安全:
由于多个执行流之间对数据的竞争操作--不安全操作,有可能造成数据的异常。
如何实现线程的安全:同步与互斥
同步:对临界资源访问的时序可控性
互斥:对临界资源同一时间的唯一访问性(原子操作)
互斥锁:
代码实现:
运行显示:
5.2条件变量:
用于实现线程间的同步---唤醒与等待
代码实现:
运行结果: