计算机基础---线程分离状态

来源:互联网 发布:网络零售市场交易额 编辑:程序博客网 时间:2024/05/12 00:52
线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中,我们采用了线程的默认属性,即为非分离状态,这种情况下,原有的线程等待创建的 线程结束。
只有当 pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线 程所等待,自己运行结束了,线程也就
终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为 pthread_attr_setdetachstate(pthread_attr_t *attr, int

detachstate)。第二个参数可选为 PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create 函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用 pthread_create 的 线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait 函数,让这个线程等待一会儿,留出足够的时间让函数 pthread_create 返回。设置一段等待时间,是 在多线程编程里常用的方法。但是注意不要使用诸如 wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。


---------------------------------------------------------分割线-------------------------------

线程数据
在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见 的。例如我们常见的变量 errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在 A 线程里输出的很可能是 B 线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程 里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。和线程数据相关的函数主要有 4 个:创建一个键;为一个键指定线程数据;从一个键读取线程数据;删除键。


-------------------------------------------------分割线-----------------------------------------------

条件变量
前一节中我们讲述了如何使用互斥锁来实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和 等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥 锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并 重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步。条件变量的结构为 pthread_cond_t,函数 pthread_cond_init ()被用来初始化一个条件变量。它的原型为:extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));


-----------------------------------------------------分割线----------------------------------------------------

信号量 /信号灯
信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。当公共资源增加时,调用函数 sem_post()增加信号量。只有当信号量值大于 0时,才能使用公共资源,使用后,函数 sem_wait()减少信号量。函数 sem_trywait()和函数 pthread_ mutex_trylock()起同样的作用,它是函数 sem_wait()的非阻塞版本。下面我们逐个介绍和信号量有关的一些函数,它们都在头文件 /usr/include/semaphore.h 中定义。信号量的数据类型为结构 sem_t,它本质上是一个长整型的数。函数 sem_init ()用来初始化一个信号量。它的原型为:extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));

int sem_getvalue(sem_t * sem, int * sval)
读取 sem 中的灯计数,存于*sval 中,并返回 0。


---------------------------------------------分割线-------------------------------------------


线程创建的 Linux 实现
我们知道,Linux 的线程实现是在核外进行的,核内提供的是创建进程的接口 do_fork()。内核提供了两个系统调用__clone()和  fork(),最终都用不同的参数调用 do_fork()核内 API。当然,要想实现线程,没有核心对多进程(其实是轻量级进程)共享数据段的支持是不行  的,因此,do_fork()提供了很多参数,包括 CLONE_VM (共享内存空间)、CLONE_FS (共享文件系统信息)、  CLONE_FILES(共享文件描述符表)、CLONE_SIGHAND(共享信号句柄表)和 CLONE_PID(共享进程 ID,仅对核内进程,即 0 号  进程有效)。当使用 fork 系统调用时,内核调用 do_fork() 不使用任何共享属性,进程拥有独立的运行环境,而使用pthread_create()来创建线程时,则最终设置了所有这些属性来调用__clone(),而这些参数又全部传给核内的 do_fork(),从  而创建的"进程"拥有共享的运行环境,只有栈是独立的,由__clone()传入。

---------------------------------------------分割线-----------------------------------------------------

Linux 线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步 、 删 除 等 操 作 都 在 核 外 pthread 库中进行。   pthread 库 使 用 一 个 管 理 线 程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为线程分配线程ID,发送线  程相关的信号(比如 Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程。

-----------------------------------------------------分割线------------------------------------------

 线程创建的 Linux 实现
我们知道,Linux 的线程实现是在核外进行的,核内提供的是创建进程的接口 do_fork()。内核提供了两个系统调用__clone()和  fork(),最终都用不同的参数调用 do_fork()核内 API。当然,要想实现线程,没有核心对多进程(其实是轻量级进程)共享数据段的支持是不行  的,因此,do_fork()提供了很多参数,包括 CLONE_VM (共享内存空间)、CLONE_FS (共享文件系统信息)、  CLONE_FILES(共享文件描述符表)、CLONE_SIGHAND(共享信号句柄表)和 CLONE_PID(共享进程 ID,仅对核内进程,即 0 号  进程有效)。当使用 fork 系统调用时,内核调用 do_fork() 不使用任何共享属性,进程拥有独立的运行环境,而使用pthread_create()来创建线程时,则最终设置了所有这些属性来调用__clone(),而这些参数又全部传给核内的 do_fork(),从  而创建的"进程"拥有共享的运行环境,只有栈是独立的,由__clone()传入。

--------------------------------------------------------------分割线------------------------------------------




0 0
原创粉丝点击