没有合适的资源?快使用搜索试试~ 我知道了~
POSIX线程详解 POSIX(可移植操作系统接口)线程是提高代码响应和性能的有力手段。在本系列中,Daniel Robbins 向您精确地展示在编程中如何使用线程。其中还涉及大量幕后细节,读完本系列文章,您完全可以运用 POSIX 线程创建多线程程序。
资源推荐
资源详情
资源评论
POSIX 线程详解
总裁
年 月
(可移植操作系统接口)线程是提高代码响应和性能的有力手段。在本系列中,
向您精确地展示在编程中如何使用线程。其中还涉及大量幕后细节,读
完本系列文章,您完全可以运用 线程创建多线程程序
线程是有趣的
了解如何正确运用线程是每一个优秀程序员必备的素质。线程类似于进程。如同进程,线
程由内核按时间分片进行管理。在单处理器系统中,内核使用时间分片来模拟线程的并发
执行,这种方式和进程的相同。而在多处理器系统中,如同多个进程,线程实际上一样可
以并发执行。
那么为什么对于大多数合作性任务,多线程比多个独立的进程更优越呢?这是因为,
线程共享相同的内存空间。不同的线程可以存取内存中的同一个变量。所以,程序中的所
有线程都可以读或写声明过的全局变量。如果曾用 !"#编写过重要代码,就会认识到这
个工具的重要性。为什么呢?虽然 !"#允许创建多个进程,但它还会带来以下通信问题$
如何让多个进程相互通信,这里每个进程都有各自独立的内存空间。对这个问题没有一个
简单的答案。虽然有许多不同种类的本地 "进程间通信),但它们都遇到两个重要障
碍:
强加了某种形式的额外内核开销,从而降低性能。
对于大多数情形,不是对于代码的“自然”扩展。通常极大地增加了程序的复杂性。
%
双重坏事$开销和复杂性都非好事。如果曾经为了支持 而对程序大动干戈过,那
么您就会真正欣赏线程提供的简单共享内存机制。由于所有的线程都驻留在同一内存空间,
线程无需进行开销大而复杂的长距离调用。只要利用简单的同步机制,程序中所有
的线程都可以读取和修改已有的数据结构。而无需将数据经由文件描述符转储或挤入紧窄
的共享内存空间。仅此一个原因,就足以让您考虑应该采用单进程多线程模式而非多进程
单线程模式。
线程是快捷的
不仅如此。线程同样还是非常快捷的。与标准 !"#相比,线程带来的开销很小。内核无
需单独复制进程的内存空间或文件描述符等等。这就节省了大量的 &时间,使得线程
创建比新进程创建快上十到一百倍。因为这一点,可以大量使用线程而无需太过于担心带
来的 &或内存不足。使用 !"#时导致的大量 &占用也不复存在。这表示只要在
程序中有意义,通常就可以创建线程。
当然,和进程一样,线程将利用多 &。如果软件是针对多处理器系统设计的,这
就真的是一大特性(如果软件是开放源码,则最终可能在不少平台上运行)。特定类型线
程程序(尤其是 &密集型程序)的性能将随系统中处理器的数目几乎线性地提高。如
果正在编写 &非常密集型的程序,则绝对想设法在代码中使用多线程。一旦掌握了线
程编码,无需使用繁琐的 和其它复杂的通信机制,就能够以全新和创造性的方法解决
编码难题。所有这些特性配合在一起使得多线程编程更有趣、快速和灵活。
线程是可移植的
如果熟悉 '()编程,就有可能知道 **"#系统调用。**"#类似于 !"#,同
时也有许多线程的特性。例如,使用 **"#,新的子进程可以有选择地共享父进程的
执行环境(内存空间,文件描述符等)。这是好的一面。但 **"#也有不足之处。正
如**"#在线帮助指出:
+**调用是特定于 '()平台的,不适用于实现可移植的程序。欲编写线程化应用程序(多线程控
制同一内存空间),最好使用实现 ,-,线程 .的库,例如 '()/ 0库。参阅
1 0* "- #。”
虽然 **"#有线程的许多特性,但它是不可移植的。当然这并不意味着代码中不
能使用它。但在软件中考虑使用 **"#时应当权衡这一事实。值得庆幸的是,正如
**"#在线帮助指出,有一种更好的替代方案:线程。如果想编写可移植的多
线程代码,代码可运行于 、2 3、'()和其它平台,线程是一种当
然之选。
第一个线程
下面是一个 线程的简单示例程序:
thread1.c
4(051 06
4(0506
4(05(06
708 0*("708 #9
:
";:5:<<#9
1 "= 0>?@=#:
1",#:
A
( B&'':
A
C"70#9
1 0*C> 0:
"1 0* "DC> 0B&''
0*(B&''##9
1 "= 0=#:
"#:
A
"1 0*E"C> 0B&''##9
1 "= E 0=#:
"#:
A
)"#:
A
要编译这个程序,只需先将程序存为 thread1.c,然后输入:
F 0,/ 0,/1 0
运行则输入:
F 0,
理解 thread1.c
0,是一个非常简单的线程程序。虽然它没有实现什么有用的功能,但可以帮助理
解线程的运行机制。下面,我们一步一步地了解这个程序是干什么的。C"#中声明了
变量 C> 0,类型是 1 0*。1 0*类型在 1 0中定义,通常称为
“线程 0G(缩写为 =0=)。可以认为它是一种线程句柄。
C> 0声明后(记住 C> 0只是一个 =0=,或是将要创建的线程的句柄),
调用 1 0* 函数创建一个真实活动的线程。不要因为 1 0* "#在
==语句内而受其迷惑。由于 1 0* "#执行成功时返回零而失败时则返回非零
值,将 1 0* "#函数调用放在 "#语句中只是为了方便地检测失败的调用。让
我们查看一下 1 0* 参数。第一个参数 DC> 0是指向 C> 0的指
针。第二个参数当前为 B&'',可用来定义线程的某些属性。由于缺省的线程属性是适用
的,只需将该参数设为 B&''。
第三个参数是新线程启动时调用的函数名。本例中,函数名为 0*("#。
当 0*("#返回时,新线程将终止。本例中,线程函数没有实现大的功能。它
仅将 = 0>?=输出 次然后退出。注意 0*("#接受 708作
为参数,同时返回值的类型也是 708。这表明可以用 708向新线程传递任意类型的
数据,新线程完成时也可返回任意类型的数据。那如何向线程传递一个任意参数?很简单。
只要利用 1 0* "#中的第四个参数。本例中,因为没有必要将任何数据传给微
不足道的 0*("#,所以将第四个参数设为 B&''。
您也许已推测到,在 1 0* "#成功返回之后,程序将包含两个线程。等一
等,两个线程?我们不是只创建了一个线程吗?不错,我们只创建了一个进程。但是主程
序同样也是一个线程。可以这样理解:如果编写的程序根本没有使用 线程,则该
程序是单线程的(这个单线程称为“主”线程)。创建一个新线程之后程序总共就有两个线
程了。
我想此时您至少有两个重要问题。第一个问题,新线程创建之后主线程如何运行。答
案,主线程按顺序继续执行下一行程序(本例中执行 ="1 0*E"##=)。第二个
问题,新线程结束时如何处理。答案,新线程先停止,然后作为其清理过程的一部分,等
待与另一个线程合并或“连接”。
现在,来看一下 1 0*E"#。正如 1 0* "#将一个线程拆分为两个,
1 0*E"#将两个线程合并为一个线程。1 0*E"#的第一个参数是 0
C> 0。第二个参数是指向 70指针的指针。如果 70指针不为
B&'',1 0*E将线程的 708返回值放置在指定的位置上。由于我们不必理会
0*("#的返回值,所以将其设为 B&''
您会注意到 0*("#花了 秒才完成。在 0*("#结束很
久之前,主线程就已经调用了 1 0*E"#。如果发生这种情况,主线程将中断(转向
睡眠)然后等待 0*("#完成。当 0*("#完成后
1 0*E"#将返回。这时程序又只有一个主线程。当程序退出时,所有新线程已经使
用 1 0*E"#合并了。这就是应该如何处理在程序中创建的每个新线程的过程。如
果没有合并一个新线程,则它仍然对系统的最大线程数限制不利。这意味着如果未对线程
做正确的清理,最终会导致 1 0* "#调用失败。
无父,无子
如果使用过 !"#系统调用,可能熟悉父进程和子进程的概念。当用 !"#创建另一个
新进程时,新进程是子进程,原始进程是父进程。这创建了可能非常有用的层次关系,尤
其是等待子进程终止时。例如,H10"#函数让当前进程等待所有子进程终止。
H10"#用来在父进程中实现简单的清理过程。
而 线程就更有意思。您可能已经注意到我一直有意避免使用“父线程”和“子线
程”的说法。这是因为 线程中不存在这种层次关系。虽然主线程可以创建一个新线
程,新线程可以创建另一个新线程,线程标准将它们视为等同的层次。所以等待子
线程退出的概念在这里没有意义。线程标准不记录任何“家族”信息。缺少家族信息
有一个主要含意:如果要等待一个线程终止,就必须将线程的 0传递给
1 0*E"#。线程库无法为您断定 0。
对大多数开发者来说这不是个好消息,因为这会使有多个线程的程序复杂化。不过不
要为此担忧。线程标准提供了有效地管理多个线程所需要的所有工具。实际上,没
有父子关系这一事实却为在程序中使用线程开辟了更创造性的方法。例如,如果有一个线
程称为线程 ,,线程 ,创建了称为线程 的线程,则线程 ,自己没有必要调用
1 0*E"#来合并线程 ,程序中其它任一线程都可以做到。当编写大量使用线程的
代码时,这就可能允许发生有趣的事情。例如,可以创建一个包含所有已停止线程的全局
“死线程列表”,然后让一个专门的清理线程专等停止的线程加到列表中。这个清理线程调
用 1 0*E"#将刚停止的线程与自己合并。现在,仅用一个线程就巧妙和有效地处
理了全部清理。
同步漫游
现在我们来看一些代码,这些代码做了一些意想不到的事情。 0的代码如下:
thread2.c
4(051 06
4(0506
4(05(06
4(0506
C>:
708 0*("708 #9
E:
";:5:<<#9
E;C>:
E;E<,:
1 "==#:
I("0(#:
1",#:
C>;E:
剩余33页未读,继续阅读
资源评论
普通网友
- 粉丝: 7
- 资源: 2
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功