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

📄 posix 线程详解2.htm

📁 POSIX平台多线程编程指南 通用开放式操作系统的多线程应用指南。
💻 HTM
📖 第 1 页 / 共 5 页
字号:
                              href="http://www-128.ibm.com/developerworks/cn/kickstart/">Java 
                              应用开发源动力 - 下载免费软件,快速启动开发</A> 
                        </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><!--END RESERVED FOR FUTURE USE INCLUDE FILES--><BR></TD></TR></TBODY></TABLE>
            <P>级别: 初级</P>
            <P><A 
            href="http://www-128.ibm.com/developerworks/cn/linux/thread/posix_thread2/#author">Daniel 
            Robbins</A>, 总裁/CEO, Gentoo Technologies, Inc.<BR></P>
            <P>2000 年 8 月 01 日</P>
            <BLOCKQUOTE>POSIX 线程是提高代码响应和性能的有力手段。在此三部分系列文章的第二篇中,Daniel Robbins 
              将说明,如何使用被称为互斥对象的灵巧小玩意,来保护线程代码中共享数据结构的完整性。</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-->
            <P><A name=N10045><SPAN class=atitle>互斥我吧!</SPAN></A></P>
            <P>在 <A 
            href="http://www-128.ibm.com/developerworks/cn/linux/thread/posix_thread1/index.html">前一篇文章中</A> 
            ,谈到了会导致异常结果的线程代码。两个线程分别对同一个全局变量进行了二十次加一。变量的值最后应该是 40,但最终值却是 
            21。这是怎么回事呢?因为一个线程不停地“取消”了另一个线程执行的加一操作,所以产生这个问题。现在让我们来查看改正后的代码,它使用 
            <B>互斥对象</B>(mutex)来解决该问题: </P><BR><A 
            name=thread3.c><B>thread3.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;
pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;

 void *thread_function(void *arg) {
  int i,j;
  for ( i=0; i&lt;20; i++) {
    pthread_mutex_lock(&amp;mymutex);
    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);
    myglobal=j;
    pthread_mutex_unlock(&amp;mymutex);
  }
  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++) {
    pthread_mutex_lock(&amp;mymutex);
    myglobal=myglobal+1;
    pthread_mutex_unlock(&amp;mymutex);
    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 线程详解2.files/blue_rule.gif" 
                  width="100%"><BR><IMG height=6 alt="" 
                  src="POSIX 线程详解2.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 线程详解2.files/c.gif" 
                  width="100%"><BR>
                  <TABLE cellSpacing=0 cellPadding=0 border=0>
                    <TBODY>
                    <TR>
                      <TD vAlign=center><IMG height=16 alt="" 
                        src="POSIX 线程详解2.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_thread2/#main"><B>回页首</B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
            <P><A name=N1005F><SPAN class=atitle>解读一下</SPAN></A></P>
            <P>如果将这段代码与 <A 
            href="http://www.ibm.com/software/developerworks/library/posix1.html">前一篇文章</A> 
            中给出的版本作一个比较,就会注意到增加了 pthread_mutex_lock() 和 pthread_mutex_unlock() 
            函数调用。在线程程序中这些调用执行了不可或缺的功能。他们提供了一种 
            <I>相互排斥</I>的方法(互斥对象即由此得名)。两个线程不能同时对同一个互斥对象加锁。 </P>
            <P>互斥对象是这样工作的。如果线程 a 试图锁定一个互斥对象,而此时线程 b 已锁定了同一个互斥对象时,线程 a 
            就将进入睡眠状态。一旦线程 b 释放了互斥对象(通过 pthread_mutex_unlock() 调用),线程 a 
            就能够锁定这个互斥对象(换句话说,线程 a 就将从 pthread_mutex_lock() 
            函数调用中返回,同时互斥对象被锁定)。同样地,当线程 a 正锁定互斥对象时,如果线程 c 试图锁定互斥对象的话,线程 c 
            也将临时进入睡眠状态。对已锁定的互斥对象上调用 pthread_mutex_lock() 
            的所有线程都将进入睡眠状态,这些睡眠的线程将“排队”访问这个互斥对象。</P>
            <P>通常使用 pthread_mutex_lock() 和 pthread_mutex_unlock() 
            来保护数据结构。这就是说,通过线程的锁定和解锁,对于某一数据结构,确保某一时刻只能有一个线程能够访问它。可以推测到,当线程试图锁定一个未加锁的互斥对象时,POSIX 
            线程库将同意锁定,而不会使线程进入睡眠状态。</P><BR><A 
            name=figure1><B>请看这幅轻松的漫画,四个小精灵重现了最近一次 pthread_mutex_lock() 
            调用的一个场面。</B></A><BR><IMG height=280 alt="" 
            src="POSIX 线程详解2.files/mutex.gif" width=600 border=0 valign="top"> 
            <BR>
            <P>图中,锁定了互斥对象的线程能够存取复杂的数据结构,而不必担心同时会有其它线程干扰。那个数据结构实际上是“冻结”了,直到互斥对象被解锁为止。pthread_mutex_lock() 
            和 pthread_mutex_unlock() 
            函数调用,如同“在施工中”标志一样,将正在修改和读取的某一特定共享数据包围起来。这两个函数调用的作用就是警告其它线程,要它们继续睡眠并等待轮到它们对互斥对象加锁。当然,除非在 
            <I>每个</I> 对特定数据结构进行读写操作的语句前后,都分别放上 pthread_mutex_lock() 和 
            pthread_mutext_unlock() 调用,才会出现这种情况。 </P><BR>
            <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
              <TBODY>
              <TR>
                <TD><IMG height=1 alt="" src="POSIX 线程详解2.files/blue_rule.gif" 
                  width="100%"><BR><IMG height=6 alt="" 
                  src="POSIX 线程详解2.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 线程详解2.files/c.gif" 
                  width="100%"><BR>
                  <TABLE cellSpacing=0 cellPadding=0 border=0>
                    <TBODY>
                    <TR>
                      <TD vAlign=center><IMG height=16 alt="" 
                        src="POSIX 线程详解2.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_thread2/#main"><B>回页首</B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
            <P><A name=N1008E><SPAN class=atitle>为什么要用互斥对象?</SPAN></A></P>
            <P>听上去很有趣,但究竟为什么要让线程睡眠呢?要知道,线程的主要优点不就是其具有独立工作、更多的时候是同时工作的能力吗?是的,确实是这样。然而,每个重要的线程程序都需要使用某些互斥对象。让我们再看一下示例程序以便理解原因所在。</P>
            <P>请看 thread_function(),循环中一开始就锁定了互斥对象,最后才将它解锁。在这个示例程序中,mymutex 用来保护 
            myglobal 的值。仔细查看 thread_function(),加一代码把 myglobal 
            复制到一个局部变量,对局部变量加一,睡眠一秒钟,在这之后才把局部变量的值传回给 myglobal。不使用互斥对象时,即使主线程在 
            thread_function() 线程睡眠一秒钟期间内对 myglobal 加一,thread_function() 
            苏醒后也会覆盖主线程所加的值。使用互斥对象能够保证这种情形不会发生。(您也许会想到,我增加了一秒钟延迟以触发不正确的结果。把局部变量的值赋给 
            myglobal 之前,实际上没有什么真正理由要求 thread_function() 
            睡眠一秒钟。)使用互斥对象的新程序产生了期望的结果:</P>
            <TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee 
            border=1>
              <TBODY>
              <TR>
                <TD><PRE><CODE class=section>
$ ./thread3
o..o..o.o..o..o.o.o.o.o..o..o..o.ooooooo
myglobal equals 40
</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>
thread_function() 加一代码:
    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);
    myglobal=j;

主线程加一代码:
    myglobal=myglobal+1;
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
            <P>如果代码是位于单线程程序中,可以预期 thread_function() 
            代码将完整执行。接下来才会执行主线程代码(或者是以相反的顺序执行)。在不使用互斥对象的线程程序中,代码可能(几乎是,由于调用了 
            sleep() 的缘故)以如下的顺序执行:</P>
            <TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee 
            border=1>
              <TBODY>
              <TR>
                <TD><PRE><CODE class=section>
    thread_function() 线程        主线程

    j=myglobal;
    j=j+1;
    printf(".");
    fflush(stdout);
    sleep(1);                     myglobal=myglobal+1;
    myglobal=j;
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
            <P>当代码以此特定顺序执行时,将覆盖主线程对 myglobal 
            的修改。程序结束后,就将得到不正确的值。如果是在操纵指针的话,就可能产生段错误。注意到 thread_function() 
            线程按顺序执行了它的所有指令。看来不象是 thread_function() 
            有什么次序颠倒。问题是,同一时间内,另一个线程对同一数据结构进行了另一个修改。</P><BR>
            <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
              <TBODY>
              <TR>

⌨️ 快捷键说明

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