Linux多线程编程
上一篇 / 下一篇 2012-08-13 10:53:31 / 个人分类:Linux
线程51Testing软件测试网 ]U'SDD)\S
EOUL`g0 线程是计算机中独立运行的最小单位,运行时占用很少的系统资源。可以把线程看成是操作系统分配CPU时间的基本单元。一个进程可以拥有一个至多个线程。它线程在进程内部共享地址空间、打开的文件描述符等资源。同时线程也有其私有的数据信息,包括:线程号、寄存器(程序计数器和堆栈指针)、堆栈、信号掩码、优先级、线程私有存储空间。51Testing软件测试网mO"}N'P'tB;LU:h)G
51Testing软件测试网PL%dW)f7r\为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?
p'I)y5Oekc6O051Testing软件测试网#p3TM3s$[RT8UJ使用多线程的理由之一是和进程相比,它是一种非常“节俭”的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种“昂贵”的多任务工作方 式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而 且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统 上,这个数据可能会有较大的区别。
;_0g5P q&ix051Testing软件测试网 a-H_LY5H-S7k Y&u7X使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据 的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它 线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可 能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
4qf0U'j D9N2G A~051Testing软件测试网OuF o j9Ut Y2Q除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:51Testing软件测试网#w:b$j"ti2x+n
51Testing软件测试网%^SWs$xF5E1)提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。
)Ab$X8Ls$_*n*y0j4j;s;b` H'~g9E5I0 2)使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。51Testing软件测试网#]8S]-xg1k![F J
0Q)j9R4?^.Ar8X'CT |0 3)改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。51Testing软件测试网,l-O/}'Eg2^)L/Y
6D4}"ll5u3e0 线程分类
nS!i7P/C,|EI051Testing软件测试网C nZf#O%@线程按照其调度者可以分为用户级线程和核心级线程两种。51Testing软件测试网e(KN] SwX#_
KD%N2X1H/sULa0 (1)用户级线程
'{,M+^X/cd:F^C4p051Testing软件测试网OD0Y7LM用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持。在这里,操作系统往往会提供 一个用户空间的线程库,该线程库提供了线程的创建、调度、撤销等功能,而内核仍然仅对进程进行管理。如果一个进程中的某一个线程调用了一个阻塞的系统调 用,那么该进程包括该进程中的其他所有线程也同时被阻塞。这种用户级线程的主要缺点是在一个进程中的多个线程的调度中无法发挥多处理器的优势。51Testing软件测试网j(l$P9dt.M
51Testing软件测试网#v k9\biq%W0V-A(2)核心级线程51Testing软件测试网(L_0N4n6L @!m
m:wqi ["u0 这种线程允许不同进程中的线程按照同一相对优先调度方法进行调度,这样就可以发挥多处理器的并发优势。51Testing软件测试网 A4A/|Gt/F0|
51Testing软件测试网.oa2zSx5p8\现在大多数系统都采用用户级线程与核心级线程并存的方法。一个用户级线程可以对应一个或几个核心级线程,也就是“一对一”或“多对一”模型。这样既可满足多处理机系统的需要,也可以最大限度地减少调度开销。
(`vC;? ao k4{#R X09I*R5LHV;d9~`0 线程创建的Linux实现
3jX2~G:AF051Testing软件测试网7Q H2C NK我们知道,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()传入。51Testing软件测试网1H3na H [(f8}m
*z4o6Y+uf)A?0 Linux线程在核内是以轻量级进程的形式存在的, 拥有独立的进程表项,而所有的创建、同步、删除等操作都在核外pthread库中进行。pthread 库使用一个管理线程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为线程分配线程ID,发送线程相关的信号 (比如Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程。51Testing软件测试网,j'I P:Z1g|8|7dw
,~_5`o ?/I0 多线程编程
}ByzE7b@051Testing软件测试网;y8G/o+K7|]f1、线程的创建和退出
wWH0c%R0t_051Testing软件测试网Vch ~&GG^+@zjpthread_create 线程创建函数
;S"SC'v C(Zy+{%YK0'D],U.Vf~ C1K7Rky0 int pthread_create (pthread_t * thread_id,__const pthread_attr_t * __attr,void *(*__start_routine) (void *),void *__restrict __arg);51Testing软件测试网7D5l/G%I-e
!\S!j9z-_m;^0 线程创建函数第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的 参数。这里,我们的函数thread 不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。当创建线程成功时,函数返回0,若不为0 则说明创建线程失败,常见的错误返回代码为EAGAIN 和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数 三和参数四确定的函数,原来的线程则继续运行下一行代码。51Testing软件测试网:E7@xQ!gm
.srUDZ5Nif3U0 pthread_join 函数,来等待一个线程的结束。
r`H ?#|5m0