⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 posix 线程详解 - 菜鸟成长轨迹 - csdnblog.htm

📁 pthread soure code
💻 HTM
📖 第 1 页 / 共 5 页
字号:
  href="http://blog.csdn.net/scorpio16/archive/2007/04.aspx">2007年04月(4)</A>
  <LI><A 
  href="http://blog.csdn.net/scorpio16/archive/2007/03.aspx">2007年03月(1)</A>
  <LI><A 
  href="http://blog.csdn.net/scorpio16/archive/2006/12.aspx">2006年12月(2)</A>
  <LI><A 
  href="http://blog.csdn.net/scorpio16/archive/2006/11.aspx">2006年11月(3)</A>
  <LI><A 
  href="http://blog.csdn.net/scorpio16/archive/2005/04.aspx">2005年04月(2)</A></LI></UL></DIV></DIV>
<DIV id=centercontent><!--done-->
<DIV class=Tag id=regionBlogTag>
<DIV style="CLEAR: both"></DIV></DIV><SPAN class=PreAndNext 
id=viewpost.ascx_PreviousAndNextEntriesUp>
<DIV align=center><A 
href="http://blog.csdn.net/scorpio16/archive/2007/04/05/1553030.aspx">上一篇:&nbsp;哇咔咔,这个校验码太帅了!!!</A>&nbsp;|&nbsp;<A 
href="http://blog.csdn.net/scorpio16/archive/2006/12/14/1443336.aspx">下一篇:&nbsp;61条面向对象设计的经验原则(zt)</A></DIV></SPAN><BR>
<SCRIPT>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</SCRIPT>

<DIV class=post>
<DIV class=postTitle><A 
href="http://blog.csdn.net/scorpio16/archive/2007/03/20/1535557.aspx"><IMG 
height=13 src="POSIX 线程详解 - 菜鸟成长轨迹 - CSDNBlog.files/zhuan.gif" width=15 
border=0>&nbsp;POSIX 线程详解</A> </DIV>
<DIV class=postText>
<P>作者:<A 
href="http://www.ibm.com/developerworks/cn/linux/thread/posix_thread1/#author"><FONT 
color=#996699>Daniel Robbins</FONT></A> (<A 
href="mailto:drobbins@gentoo.org?subject=POSIX 线程详解&amp;cc=drobbins@gentoo.org"><FONT 
color=#5c81a7>drobbins@gentoo.org</FONT></A>), 总裁/CEO, Gentoo Technologies, 
Inc.</P>
<H1>第一部分:一种支持内存共享的简捷工具</H1>
<BLOCKQUOTE>POSIX(可移植操作系统接口)线程是提高代码响应和性能的有力手段。在本系列中,Daniel<BR>Robbins 
  向您精确地展示在编程中如何使用线程。其中还涉及大量幕后细节,读完本系列文章,<BR>您完全可以运用 POSIX 线程创建多线程程序。</BLOCKQUOTE><!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
<H2><A name=N10046><SPAN class=atitle>线程是有趣的</SPAN></A></H2>
<P>了解如何正确运用线程是每一个优秀程序员必备的素质。线程类似于进程。如同进程,线程由内核按时间<BR>分片进行管理。在单处理器系统中,内核使用时间分片来模拟线程的并发执行,这种方式和进程的相同。<BR>而在多处理器系统中,如同多个进程,线程实际上一样可以并发执行。</P>
<P>那么为什么对于大多数合作性任务,多线程比多个独立的进程更优越呢?这是因为,线程共享相同的内存<BR>空间。不同的线程可以存取内存中的同一个变量。所以,程序中的所有线程都可以读或写声明过的全局变<BR>量。如果曾用 
fork() 编写过重要代码,就会认识到这个工具的重要性。为什么呢?虽然 fork() 允许创建多<BR>个进程,但它还会带来以下通信问题: 
如何让多个进程相互通信,这里每个进程都有各自独立的内存空间。<BR>对这个问题没有一个简单的答案。虽然有许多不同种类的本地 IPC 
(进程间通信),但它们都遇到两个重<BR>要障碍:</P>
<UL>
  <LI>强加了某种形式的额外内核开销,从而降低性能。 
  <LI>对于大多数情形,IPC 不是对于代码的“自然”扩展。通常极大地增加了程序的复杂性。 </LI></UL>
<P>双重坏事: 开销和复杂性都非好事。如果曾经为了支持 IPC 
而对程序大动干戈过,那么您就会真正欣赏线<BR>程提供的简单共享内存机制。由于所有的线程都驻留在同一内存空间,POSIX 
线程无需进行开销大而复杂<BR>的长距离调用。只要利用简单的同步机制,程序中所有的线程都可以读取和修改已有的数据结构。而无需<BR>将数据经由文件描述符转储或挤入紧窄的共享内存空间。仅此一个原因,就足以让您考虑应该采用单进程<BR>/多线程模式而非多进程/单线程模式。</P>
<H2><A name=N1005E><SPAN class=atitle>线程是快捷的</SPAN></A></H2>
<P>不仅如此。线程同样还是非常快捷的。与标准 fork() 相比,线程带来的开销很小。内核无需单独复制进程<BR>的内存空间或文件描述符等等。这就节省了大量的 
CPU 时间,使得线程创建比新进程创建快上十到一百<BR>倍。因为这一点,可以大量使用线程而无需太过于担心带来的 CPU 或内存不足。使用 fork() 
时导致的大<BR>量 CPU 占用也不复存在。这表示只要在程序中有意义,通常就可以创建线程。</P>
<P>当然,和进程一样,线程将利用多 
CPU。如果软件是针对多处理器系统设计的,这就真的是一大特性(如<BR>果软件是开放源码,则最终可能在不少平台上运行)。特定类型线程程序(尤其是 CPU 
密集型程序)的<BR>性能将随系统中处理器的数目几乎线性地提高。如果正在编写 CPU 
非常密集型的程序,则绝对想设法在<BR>代码中使用多线程。一旦掌握了线程编码,无需使用繁琐的 IPC 
和其它复杂的通信机制,就能够以全新<BR>和创造性的方法解决编码难题。所有这些特性配合在一起使得多线程编程更有趣、快速和灵活。</P>
<H2><A name=N1006A><SPAN class=atitle>线程是可移植的</SPAN></A></H2>
<P>如果熟悉 Linux 编程,就有可能知道 __clone() 系统调用。__clone() 类似于 
fork(),同时也有许多线程<BR>的特性。例如,使用 
__clone(),新的子进程可以有选择地共享父进程的执行环境(内存空间,文件描述<BR>符等)。这是好的一面。但 __clone() 
也有不足之处。正如__clone() 在线帮助指出:</P>
<BLOCKQUOTE>“__clone 调用是特定于 Linux 
  平台的,不适用于实现可移植的程序。欲编写线程化应用程序(多<BR>线程控制同一内存空间),最好使用实现 POSIX 1003.1c 线程 API 的库,例如 
  Linux-Threads <BR>库。参阅 pthread_create(3thr)。” </BLOCKQUOTE>
<P>虽然 __clone() 有线程的许多特性,但它是不可移植的。当然这并不意味着代码中不能使用它。但在软件<BR>中考虑使用 __clone() 
时应当权衡这一事实。值得庆幸的是,正如 __clone() 在线帮助指出,有一种更好<BR>的替代方案:POSIX 
线程。如果想编写<STRONG>可移植的</STRONG>多线程代码,代码可运行于Solaris、FreeBSD、Linux <BR>和其它平台,POSIX 
线程是一种当然之选。</P>
<H2><A name=N1007C><SPAN class=atitle>第一个线程</SPAN></A></H2><BR>
<P>下面是一个 POSIX 线程的简单示例程序:<BR><BR><A 
name=N10085><STRONG>thread1.c</STRONG></A></P><PRE class=displaycode>#include &lt;pthread.h&gt;<BR>#include &lt;stdlib.h&gt;<BR>#include &lt;unistd.h&gt;<BR> void *thread_function(void *arg) {<BR>  int i;<BR>  for ( i=0; i&lt;20; i++) {<BR>    printf("Thread says hi!\n");<BR>    sleep(1);<BR>  }<BR>  return NULL;<BR>}<BR>int main(void) {<BR>  pthread_t mythread;<BR>  <BR>  if ( pthread_create( &amp;mythread, NULL, thread_function, NULL) ) {<BR>    printf("error creating thread.");<BR>    abort();<BR>  }<BR>  if ( pthread_join ( mythread, NULL ) ) {<BR>    printf("error joining thread.");<BR>    abort();<BR>  }<BR>  exit(0);<BR>}</PRE>
<P>要编译这个程序,只需先将程序存为 thread1.c,然后输入:</P><PRE class=displaycode>$ gcc thread1.c -o thread1 -lpthread</PRE>
<P>运行则输入:</P><PRE class=displaycode>$ ./thread1</PRE>
<H2><A name=N1009D><SPAN class=atitle>理解 thread1.c</SPAN></A></H2>
<P>thread1.c 
是一个非常简单的线程程序。虽然它没有实现什么有用的功能,但可以帮助理解线程的运行机<BR>制。下面,我们一步一步地了解这个程序是干什么的。main() 
中声明了变量 mythread,类型是 pthread_t。<BR>pthread_t 类型在 pthread.h 中定义,通常称为“线程 id”(缩写为 
"tid")。可以认为它是一种线程句柄。</P>
<P>mythread 声明后(记住 mythread 只是一个 "tid",或是将要创建的线程的句柄),调用 pthread_create 
<BR>函数创建一个真实活动的线程。不要因为 pthread_create() 在 "if" 语句内而受其迷惑。由于 pthread_create() 
<BR>执行成功时返回零而失败时则返回非零值,将 pthread_create() 函数调用放在 if() 
语句中只是为了方便<BR>地检测失败的调用。让我们查看一下 pthread_create 参数。第一个参数 &amp;mythread 是指向 mythread 
<BR>的指针。第二个参数当前为 NULL,可用来定义线程的某些属性。由于缺省的线程属性是适用的,只需<BR>将该参数设为 NULL。</P>
<P>第三个参数是新线程启动时调用的函数名。本例中,函数名为 thread_function()。当 
thread_function()<BR>返回时,新线程将终止。本例中,线程函数没有实现大的功能。它仅将 "Thread says hi!" 输出 20 
次然<BR>后退出。注意 thread_function() 接受 void * 作为参数,同时返回值的类型也是 void *。这表明可以用 <BR>void 
* 向新线程传递任意类型的数据,新线程完成时也可返回任意类型的数据。那如何向线程传递一个任<BR>意参数?很简单。只要利用 pthread_create() 
中的第四个参数。本例中,因为没有必要将任何数据传给<BR>微不足道的 thread_function(),所以将第四个参数设为 NULL。</P>
<P>您也许已推测到,在 pthread_create() 成功返回之后,程序将包含两个线程。等一等, 
<STRONG>两个</STRONG>线程?我<BR>们不是只创建了一个线程吗?不错,我们只创建了一个进程。但是主程序同样也是一个线程。可以这样<BR>理解:如果编写的程序根本没有使用 
POSIX 线程,则该程序是单线程的(这个单线程称为“主”线程)<BR>。创建一个新线程之后程序总共就有两个线程了。 </P>
<P>我想此时您至少有两个重要问题。第一个问题,新线程创建之后主线程如何运行。答案,主线程按顺序<BR>继续执行下一行程序(本例中执行 "if 
(pthread_join(...))")。第二个问题,新线程结束时如何处理。答<BR>案,新线程先停止,然后作为其清理过程的一部分,等待与另一个线程合并或“连接”。</P>
<P>现在,来看一下 pthread_join()。正如 pthread_create() 将一个线程拆分为两个, pthread_join() 
将<BR>两个线程合并为一个线程。pthread_join() 的第一个参数是 tid mythread。第二个参数是指向 void 指<BR>针的指针。如果 
void 指针不为 NULL,pthread_join 将线程的 void * 返回值放置在指定的位置上。<BR>由于我们不必理会 
thread_function() 的返回值,所以将其设为 NULL.</P>
<P>您会注意到 thread_function() 花了 20 秒才完成。在 thread_function() 结束很久之前,主线程就已经<BR>调用了 
pthread_join()。如果发生这种情况,主线程将中断(转向睡眠)然后等待 thread_function() 完<BR>成。当 
thread_function() 完成后, pthread_join() 将返回。这时程序又只有一个主线程。当程序退出时,<BR>所有新线程已经使用 
pthread_join() 
合并了。这就是应该如何处理在程序中创建的每个新线程的过程。<BR>如果没有合并一个新线程,则它仍然对系统的最大线程数限制不利。这意味着如果未对线程做正确的清<BR>理,最终会导致 
pthread_create() 调用失败。</P>
<H2><A name=N100BB><SPAN class=atitle>无父,无子</SPAN></A></H2>
<P>如果使用过 fork() 系统调用,可能熟悉父进程和子进程的概念。当用 fork() 
创建另一个新进程时,新<BR>进程是子进程,原始进程是父进程。这创建了可能非常有用的层次关系,尤其是等待子进程终止时。<BR>例如,waitpid() 
函数让当前进程等待所有子进程终止。waitpid() 用来在父进程中实现简单的清理过程。</P>
<P>而 POSIX 线程就更有意思。您可能已经注意到我一直有意避免使用“父线程”和“子线程”的说法。<BR>这是因为 POSIX 
线程中不存在这种层次关系。虽然主线程可以创建一个新线程,新线程可以创建另一<BR>个新线程,POSIX 
线程标准将它们视为等同的层次。所以等待子线程退出的概念在这里没有意义。<BR>POSIX 
线程标准不记录任何“家族”信息。缺少家族信息有一个主要含意:如果要等待一个线程终止,<BR>就必须将线程的 tid 传递给 
pthread_join()。线程库无法为您断定 tid。</P>
<P>对大多数开发者来说这不是个好消息,因为这会使有多个线程的程序复杂化。不过不要为此担忧。<BR>POSIX 
线程标准提供了有效地管理多个线程所需要的所有工具。实际上,没有父/子关系这一事实却为<BR>在程序中使用线程开辟了更创造性的方法。例如,如果有一个线程称为线程 
1,线程 1 创建了称为线<BR>程 2 的线程,则线程 1 自己没有必要调用 pthread_join() 来合并线程 
2,程序中其它任一线程都可<BR>以做到。当编写大量使用线程的代码时,这就可能允许发生有趣的事情。例如,可以创建一个包含所<BR>有已停止线程的全局“死线程列表”,然后让一个专门的清理线程专等停止的线程加到列表中。这个<BR>清理线程调用 
pthread_join() 将刚停止的线程与自己合并。现在,仅用一个线程就巧妙和有效地处理<BR>了全部清理。</P>
<H2><A name=N100CA><SPAN class=atitle>同步漫游</SPAN></A></H2><BR>
<P>现在我们来看一些代码,这些代码做了一些意想不到的事情。thread2.c 的代码如下:<BR><BR><A 
name=N100D3><STRONG>thread2.c</STRONG></A></P><PRE class=displaycode>#include &lt;pthread.h&gt;<BR>#include &lt;stdlib.h&gt;<BR>#include &lt;unistd.h&gt;<BR>#include &lt;stdio.h&gt;<BR>int myglobal;<BR> void *thread_function(void *arg) {<BR>  int i,j;<BR>  for ( i=0; i&lt;20; i++) {<BR>    j=myglobal;<BR>    j=j+1;<BR>    printf(".");<BR>    fflush(stdout);<BR>    sleep(1);<BR>    myglobal=j;<BR>  }<BR>  return NULL;<BR>}<BR>int main(void) {<BR>  pthread_t mythread;<BR>  int i;<BR>  if ( pthread_create( &amp;mythread, NULL, thread_function, NULL) ) {<BR>    printf("error creating thread.");<BR>    abort();<BR>  }<BR>  for ( i=0; i&lt;20; i++) {<BR>    myglobal=myglobal+1;<BR>    printf("o");<BR>    fflush(stdout);<BR>    sleep(1);<BR>  }<BR>  if ( pthread_join ( mythread, NULL ) ) {<BR>    printf("error joining thread.");<BR>    abort();<BR>  }<BR>  printf("\nmyglobal equals %d\n",myglobal);<BR>  exit(0);<BR>}</PRE><A 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -