【Linux】线程和死锁

来源:互联网 发布:大数据与人工智能关系 编辑:程序博客网 时间:2024/05/16 10:57
线程
*******引入线程的原因
     在OS中能拥有资源和独立运行的基本单位是进程,然而随着计算机技术的发展,进程出现了很多弊端。
1、更好的支持SMP和多核:进程在SMP上可以同时使用多个cpu或者核执行各个线程,很好的支持并行。
2、由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入轻型进程。线程减小上下文切换的开销:同一进程的线程共享资源、状态记录等;实现同类任务的内存块共享,对逻辑内存重用,而进程间只能实现对物理内存的分时共享。

     因此出现了能独立运行的基本单位——线程(Threads)。
*******概念
线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。因此线程有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。
一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
由于同一进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一 个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
1、文件描述符表
2、每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)
3、当前工作目的
4、用户id和组id
线程概念及必须独立拥有的东西:
1、调度的基本单位,必须有自己独立的硬件上下文(切出去保存为了恢复)
2、必须有自己的私有栈结构,不影响
3、信号屏蔽字:2个线程有2个pcd
4、调度优先级
5、线程id
线程库函数是由POSIX标准定义的,称为POSIX thread或者pthread。在Linux 上线 程函数位于libpthread共享库中,因此在编译时要加上-lpthread选项。(gcc -o)
*******进程和线程的区别
1、进程:是分配系统资源(一个pcd)的基本单位,是一个动态概念,竟争计算机系统资源的基本单位。
      线程:是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程是操作系统(cpu)调度的基本单位。
2、进程:每一个进程都有一个自己的地址空间,不依赖于线程而独立存在进程空间的大小,只与处理机的位数有关。
      线程:线程是进程的一部分,没有自己的地址空间,与进程内的其他线程一起共享分配给该进程的所有资源
3、进程:进程间相互独立,强调资源共享。
     线程:同一进程的各线程间共享,强调资源独占。某进程内的线程在其它进程不可见。
4、进程:进程间不能直接进行通信,需要通过管道、消息队列、信号量和共享内存等进行通信,通信共享数据复杂度大。
     线程:线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
5、调度和切换:线程上下文切换比进程上下文切换要快得多。在多线程OS中,进程不是一个可执行的实体。
6、进程:至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。
      线程:线程只有 3 个基本状态:就绪,执行,阻塞
*******线程的创建、终止、等待和分离
了解以下几个函数:
pthread_create:成功返回0,失败返回错误号。

创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线程函数。
thread:线程标识符;
attr:线程属性设置;
start_routine:线程函数的起始地址;
arg:传递给start_routine的参数;
       以前学过的系统函数都是成功返回0,失败返回-1,而错误号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号,由于pthread_create的错误码不保存在errno中,因此不能直接用perror(3)打印错误信息,可以先用strerror(3)把错误码转换成错误信息再打印。
 
pthread_self:获得线程id


创建线程的例子:



注意:

1、任意一个线程函数中调用exit或_exit,整个进程的所有线程都终止。

2、从main函数中return,相当于调用exit,终止进程。

进程一旦退出,线程就没用了,所有线程都终止,故想要退出线程需要以下方法。
线程的终止:有3种方式
1、直接return。value_ptr所指向的单元里存放的是thread线程函数的返回值。注意:return方式建议在非主线程中。
     eg:return (void*)1://传退出码,退出码在0-255
2、pthread_concel:一个线程可以调用pthread_cancel终止同一进程中的另一个线程


线程调用pthread_cancel异常终掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED。
eg:pthead_cancel(tid);//线程可以被取消,退出码一定是-1,退出信息存在pcd或相关结构

3、pthread_exit:线程可以调用pthread_exit终止自己

value_ptr所指向的单元存放的是传给pthread_exit的参数。如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数。
eg:pthead_exit((void*)1);//pthead_exit,传退出码
      如果一个可结合线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收,所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源。

      pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

线程没有释放的话,也会出现内存泄漏,必须进行线程的等待
线程等待:pthread_join函数,成功返回0,失败返回错误号

线程分离:pthread_detach,调用该函数的线程将挂起等待,直到id为thread的线程终止。成功返回0,失败返回错误号。
线程两方都可以pthead_detach;分离后,仍是原先进程中的,进程退出后线程也会退出。默认情况下,线程被创建成可结合的。为了避免存储器泄漏,每个可结合线程都应该要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。
       由于调用pthread_join后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。

        一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach 状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。对一个尚未detach的线程调用pthread_join或pthread_detach都可以把该线程置为detach状态,也就是说,不能对同一线程调用两次pthread_join,或者如果已经对一个线程调用 了pthread_detach就不能再调用pthread_join了。

进行线程的终止、等待:

运行结果如下:

      在Web服务器中当主线程为每个新来的连接请求创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的连接请求),这时可以在子线程中加入代码 pthread_detach(pthread_self())或者父线程调用pthread_detach(thread_id)(非阻塞,可立即返回)这将该子线程的状态设置为分离的(detached),如此一来,该线程运行结束后会自动释放所有资源。
死锁
*******死锁的概念
如果一组进程中的每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,那么该组进程是死锁的。


*******死锁的常见表现:

死锁不仅会发生多个进程中,也会发生在一个进程中。

(1)多进程死锁:有进程A,进程B,进程A拥有资源1,需要请求正在被进程B占有的资源2。而进程B拥有资源2,请求正在被进程A战友的资源1。两个进程都在等待对方释放资源后请求该资源,而相互僵持,陷入死锁。

(2)单进程死锁:进程A拥有资源1,而它又在请求资源1,而它所请求的资源1必须等待该资源使用完毕得到释放后才可被请求。这样,就陷入了自己的死锁。


*******产生死锁的原因:

(1)进程推进顺序不当造成死锁。

(2)竞争不可抢占性资源引起死锁。

(3)竞争可消耗性资源引起死锁。


*******死锁的四个必要条件(四个条件四者不可缺一):

(1)互斥条件。某段时间内,一个资源一次只能被一个进程访问。

(2)请求和保持条件。进程A已经拥有至少一个资源,此时又去申请其他资源,而该资源又正在被进程使用,此时请求进程阻塞,但对自己已经获得的资源保持不放。

(3)不可抢占资源。进程已获得的资源在未使用完不能被抢占,只能在自己使用完时由自己释放。

(4)循环等待序列。存在一个循环等待序列P0P1P2……Pn,P0请求正在被进程P1占有的资源,P1请求正在被P2占有的资源……Pn正在请求被进程P0占有的资源。


*******避免死锁的方法:

(1)终止(或撤销)进程。终止(或撤销)系统中的一个或多个死锁进程,直至打破循环环路,使系统从死锁状态中解除出来。

(2)抢占资源。从一个或多个进程中抢占足够数量的资源,分配给死锁进程,以打破死锁状态。
(3)不用死锁。吐舌头
0 0
原创粉丝点击