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

📄 posix 线程详解1.htm

📁 POSIX平台多线程编程指南 通用开放式操作系统的多线程应用指南。
💻 HTM
📖 第 1 页 / 共 5 页
字号:
            函数调用放在 if() 语句中只是为了方便地检测失败的调用。让我们查看一下 pthread_create 参数。第一个参数 
            &mythread 是指向 mythread 的指针。第二个参数当前为 
            NULL,可用来定义线程的某些属性。由于缺省的线程属性是适用的,只需将该参数设为 NULL。</P>
            <P>第三个参数是新线程启动时调用的函数名。本例中,函数名为 thread_function()。当 thread_function() 
            返回时,新线程将终止。本例中,线程函数没有实现大的功能。它仅将 "Thread says hi!" 输出 20 次然后退出。注意 
            thread_function() 接受 void * 作为参数,同时返回值的类型也是 void *。这表明可以用 void * 
            向新线程传递任意类型的数据,新线程完成时也可返回任意类型的数据。那如何向线程传递一个任意参数?很简单。只要利用 
            pthread_create() 中的第四个参数。本例中,因为没有必要将任何数据传给微不足道的 
            thread_function(),所以将第四个参数设为 NULL。</P>
            <P>您也许已推测到,在 pthread_create() 成功返回之后,程序将包含两个线程。等一等, <B>两个</B> 
            线程?我们不是只创建了一个线程吗?不错,我们只创建了一个进程。但是主程序同样也是一个线程。可以这样理解:如果编写的程序根本没有使用 
            POSIX 线程,则该程序是单线程的(这个单线程称为“主”线程)。创建一个新线程之后程序总共就有两个线程了。 </P>
            <P>我想此时您至少有两个重要问题。第一个问题,新线程创建之后主线程如何运行。答案,主线程按顺序继续执行下一行程序(本例中执行 "if 
            (pthread_join(...))")。第二个问题,新线程结束时如何处理。答案,新线程先停止,然后作为其清理过程的一部分,等待与另一个线程合并或“连接”。</P>
            <P>现在,来看一下 pthread_join()。正如 pthread_create() 将一个线程拆分为两个, 
            pthread_join() 将两个线程合并为一个线程。pthread_join() 的第一个参数是 tid 
            mythread。第二个参数是指向 void 指针的指针。如果 void 指针不为 NULL,pthread_join 将线程的 
            void * 返回值放置在指定的位置上。由于我们不必理会 thread_function() 的返回值,所以将其设为 NULL.</P>
            <P>您会注意到 thread_function() 花了 20 秒才完成。在 thread_function() 
            结束很久之前,主线程就已经调用了 pthread_join()。如果发生这种情况,主线程将中断(转向睡眠)然后等待 
            thread_function() 完成。当 thread_function() 完成后, pthread_join() 
            将返回。这时程序又只有一个主线程。当程序退出时,所有新线程已经使用 pthread_join() 
            合并了。这就是应该如何处理在程序中创建的每个新线程的过程。如果没有合并一个新线程,则它仍然对系统的最大线程数限制不利。这意味着如果未对线程做正确的清理,最终会导致 
            pthread_create() 调用失败。</P><BR>
            <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
              <TBODY>
              <TR>
                <TD><IMG height=1 alt="" src="POSIX 线程详解1.files/blue_rule.gif" 
                  width="100%"><BR><IMG height=6 alt="" 
                  src="POSIX 线程详解1.files/c.gif" width=8 
            border=0></TD></TR></TBODY></TABLE>
            <TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
              <TBODY>
              <TR align=right>
                <TD><IMG height=4 alt="" src="POSIX 线程详解1.files/c.gif" 
                  width="100%"><BR>
                  <TABLE cellSpacing=0 cellPadding=0 border=0>
                    <TBODY>
                    <TR>
                      <TD vAlign=center><IMG height=16 alt="" 
                        src="POSIX 线程详解1.files/u_bold.gif" width=16 
                      border=0><BR></TD>
                      <TD vAlign=top align=right><A class=fbox 
                        href="http://www-128.ibm.com/developerworks/cn/linux/thread/posix_thread1/index.html#main"><B>回页首</B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
            <P><A name=N100BB><SPAN class=atitle>无父,无子</SPAN></A></P>
            <P>如果使用过 fork() 系统调用,可能熟悉父进程和子进程的概念。当用 fork() 
            创建另一个新进程时,新进程是子进程,原始进程是父进程。这创建了可能非常有用的层次关系,尤其是等待子进程终止时。例如,waitpid() 
            函数让当前进程等待所有子进程终止。waitpid() 用来在父进程中实现简单的清理过程。</P>
            <P>而 POSIX 线程就更有意思。您可能已经注意到我一直有意避免使用“父线程”和“子线程”的说法。这是因为 POSIX 
            线程中不存在这种层次关系。虽然主线程可以创建一个新线程,新线程可以创建另一个新线程,POSIX 
            线程标准将它们视为等同的层次。所以等待子线程退出的概念在这里没有意义。POSIX 
            线程标准不记录任何“家族”信息。缺少家族信息有一个主要含意:如果要等待一个线程终止,就必须将线程的 tid 传递给 
            pthread_join()。线程库无法为您断定 tid。</P>
            <P>对大多数开发者来说这不是个好消息,因为这会使有多个线程的程序复杂化。不过不要为此担忧。POSIX 
            线程标准提供了有效地管理多个线程所需要的所有工具。实际上,没有父/子关系这一事实却为在程序中使用线程开辟了更创造性的方法。例如,如果有一个线程称为线程 
            1,线程 1 创建了称为线程 2 的线程,则线程 1 自己没有必要调用 pthread_join() 来合并线程 
            2,程序中其它任一线程都可以做到。当编写大量使用线程的代码时,这就可能允许发生有趣的事情。例如,可以创建一个包含所有已停止线程的全局“死线程列表”,然后让一个专门的清理线程专等停止的线程加到列表中。这个清理线程调用 
            pthread_join() 将刚停止的线程与自己合并。现在,仅用一个线程就巧妙和有效地处理了全部清理。</P><BR>
            <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
              <TBODY>
              <TR>
                <TD><IMG height=1 alt="" src="POSIX 线程详解1.files/blue_rule.gif" 
                  width="100%"><BR><IMG height=6 alt="" 
                  src="POSIX 线程详解1.files/c.gif" width=8 
            border=0></TD></TR></TBODY></TABLE>
            <TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
              <TBODY>
              <TR align=right>
                <TD><IMG height=4 alt="" src="POSIX 线程详解1.files/c.gif" 
                  width="100%"><BR>
                  <TABLE cellSpacing=0 cellPadding=0 border=0>
                    <TBODY>
                    <TR>
                      <TD vAlign=center><IMG height=16 alt="" 
                        src="POSIX 线程详解1.files/u_bold.gif" width=16 
                      border=0><BR></TD>
                      <TD vAlign=top align=right><A class=fbox 
                        href="http://www-128.ibm.com/developerworks/cn/linux/thread/posix_thread1/index.html#main"><B>回页首</B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
            <P><A name=N100CA><SPAN class=atitle>同步漫游</SPAN></A></P>
            <P>现在我们来看一些代码,这些代码做了一些意想不到的事情。thread2.c 的代码如下:</P><BR><A 
            name=N100D3><B>thread2.c</B></A><BR>
            <TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee 
            border=1>
              <TBODY>
              <TR>
                <TD><PRE><CODE class=section>
#include &lt;pthread.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdio.h&gt;

int myglobal;

 void *thread_function(void *arg) {
  int i,j;
  for ( i=0; i&lt;20; i++) {
    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);
    myglobal=j;
  }
  return NULL;
}

int main(void) {

  pthread_t mythread;
  int i;

  if ( pthread_create( &amp;mythread, NULL, thread_function, NULL) ) {
    printf("error creating thread.");
    abort();
  }

  for ( i=0; i&lt;20; i++) {
    myglobal=myglobal+1;
    printf("o");
    fflush(stdout);
    sleep(1);
  }

  if ( pthread_join ( mythread, NULL ) ) {
    printf("error joining thread.");
    abort();
  }

  printf("\nmyglobal equals %d\n",myglobal);

  exit(0);

}
</CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>
            <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
              <TBODY>
              <TR>
                <TD><IMG height=1 alt="" src="POSIX 线程详解1.files/blue_rule.gif" 
                  width="100%"><BR><IMG height=6 alt="" 
                  src="POSIX 线程详解1.files/c.gif" width=8 
            border=0></TD></TR></TBODY></TABLE>
            <TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
              <TBODY>
              <TR align=right>
                <TD><IMG height=4 alt="" src="POSIX 线程详解1.files/c.gif" 
                  width="100%"><BR>
                  <TABLE cellSpacing=0 cellPadding=0 border=0>
                    <TBODY>
                    <TR>
                      <TD vAlign=center><IMG height=16 alt="" 
                        src="POSIX 线程详解1.files/u_bold.gif" width=16 
                      border=0><BR></TD>
                      <TD vAlign=top align=right><A class=fbox 
                        href="http://www-128.ibm.com/developerworks/cn/linux/thread/posix_thread1/index.html#main"><B>回页首</B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
            <P><A name=N100DD><SPAN class=atitle>理解 thread2.c</SPAN></A></P>
            <P>如同第一个程序,这个程序创建一个新线程。主线程和新线程都将全局变量 myglobal 加一 20 
            次。但是程序本身产生了某些意想不到的结果。编译代码请输入:</P>
            <TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee 
            border=1>
              <TBODY>
              <TR>
                <TD><PRE><CODE class=section>
$ gcc thread2.c -o thread2 -lpthread
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
            <P>运行请输入:</P>
            <TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee 
            border=1>
              <TBODY>
              <TR>
                <TD><PRE><CODE class=section>
$ ./thread2
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
            <P>输出:</P>
            <TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee 
            border=1>
              <TBODY>
              <TR>
                <TD><PRE><CODE class=section>
$ ./thread2
..o.o.o.o.oo.o.o.o.o.o.o.o.o.o..o.o.o.o.o
myglobal equals 21
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
            <P>非常意外吧!因为 myglobal 从零开始,主线程和新线程各自对其进行了 20 次加一, 程序结束时 myglobal 
            值应当等于 40。由于 myglobal 输出结果为 21,这其中肯定有问题。但是究竟是什么呢?</P>
            <P>放弃吗?好,让我来解释是怎么一回事。首先查看函数 thread_function()。注意如何将 myglobal 复制到局部变量 
            "j" 了吗? 接着将 j 加一, 再睡眠一秒,然后到这时才将新的 j 值复制到 
            myglobal?这就是关键所在。设想一下,如果主线程就在新线程将 myglobal 值复制给 j <B>后</B> 立即将 
            myglobal 加一,会发生什么?当 thread_function() 将 j 的值写回 myglobal 
            时,就覆盖了主线程所做的修改。 </P>
            <P>当编写线程程序时,应避免产生这种无用的副作用,否则只会浪费时间(当然,除了编写关于 POSIX 
            线程的文章时有用)。那么,如何才能排除这种问题呢?</P>
            <P>由于是将 myglobal 复制给 j 并且等了一秒之后才写回时产生问题,可以尝试避免使用临时局部变量并直接将 myglobal 
            加一。虽然这种解决方案对这个特定例子适用,但它还是不正确。如果我们对 myglobal 

⌨️ 快捷键说明

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