多线程同步问题 条件变量和信号量

Unix/Linux编程实践一书 p440 14.5.2,介绍了使用条件变量进行线程同步。

程序是开两个线程分别统计两个文件的字数,都统计完后,主线程得出总文字数。

现在想要一个线程统计完成之后立即能够通知主线程,从而主线程能够立即打印出已经

完成的文件信息。就像各州选举,可以及时通告已经结束的州的选情一个道理。

 

书中程序的思想是

由muterx保护一个mailbox,子线程获得mailbox写权力后将统计好的字数信息写mailbox后,通知(pthread_cond_signal)主线程。

主线程一直在等待子线程写好mailbox的信号(pthread_cond_wait),然后读。读完mailbox后给出已读完信号。

注意子线程在主线程读mailbox的时候不能写mailbox,它要等待主线程的已读完信号。

 

子线程写完它的信息后结束。

主线程读完它需要的所有信息后结束(例如两个线程统计,则需要读两个)。

 

pthread_cond_wait(&flag,  &lock) //特别注意pthread_cond_wait 会解锁lock

pthread_cond_signal(&flag, &lock)

 

但是感觉条件变量并不是很好的方法,例如对于书中的程序两个线程统计两个文件没有问题,但是如果

3个线程统计3个文件,就可能发生死锁。文件名之类都没变,只是3个线程统计3个文件。

代码如下:

  1 /* twordcount4.c - threaded word counter for two files.    

 2    *         - Version 4: condition variable allows counter
 3   *                            functions to report results early 
 4    */
 5 
 6  #include   < stdio.h >
 7  #include   < pthread.h >
 8  #include   < ctype.h >
 9 
10  struct  arg_set {         /*  two values in one arg */
11           char   * fname;     /*  file to examine     */
12           int   count;     /*  number of words     */
13           int  tid;
14  };
15 
16  struct  arg_set   * mailbox  =  NULL;
17  pthread_mutex_t  lock   =  PTHREAD_MUTEX_INITIALIZER;
18  pthread_cond_t  flag  =  PTHREAD_COND_INITIALIZER;
19 
20  main( int  ac,  char   * av[])
21  {
22      pthread_t      t1, t2, t3;         /*  two threads  */
23       struct  arg_set args1, args2, args3;     /*  two argsets  */
24       void             * count_words( void   * );
25       int             reports_in  =   0 ;
26       int            total_words  =   0 ;
27 
28       if  ( ac  !=   4  ){
29          printf( " usage: %s file1 file2 file3\n " , av[ 0 ]);
30          exit( 1 );
31      }
32      pthread_mutex_lock( & lock );     /*  lock the report box now  */
33 
34      args1.fname  =  av[ 1 ];
35      args1.count  =   0 ;
36      args1.tid  =   1 ;
37      pthread_create( & t1, NULL, count_words, ( void   * & args1);
38 
39      args2.fname  =  av[ 2 ];
40      args2.count  =   0 ;
41      args2.tid  =   2 ;
42      pthread_create( & t2, NULL, count_words, ( void   * & args2);
43 
44      args3.fname  =  av[ 2 ];
45      args3.count  =   0 ;
46      args3.tid  =   3 ;
47      pthread_create( & t3, NULL, count_words, ( void   * & args3);
48 
49 
50       while ( reports_in  <   3  ){
51          printf( " MAIN: waiting for flag to go up\n " );
52          pthread_cond_wait( & flag,  & lock );  /*  wait for notify  */
53          printf( " MAIN: Wow! flag was raised, I have the lock\n " );
54          sleep( 10 );
55          printf( " %7d: %s\n " , mailbox -> count, mailbox -> fname);
56          total_words  +=  mailbox -> count;
57           if  ( mailbox  ==   & args1) 
58              pthread_join(t1,NULL);
59           if  ( mailbox  ==   & args2) 
60              pthread_join(t2,NULL);
61          sleep( 10 );
62          mailbox  =  NULL;
63          printf( " Ok,I have read the mail\n " );
64          pthread_cond_signal( & flag);     /*  announce state change  */
65            reports_in ++ ;
66      }
67      printf( " %7d: total words\n " , total_words);
68  }
69  void   * count_words( void   * a)
70  {
71       struct  arg_set  * args  =  a;     /*  cast arg back to correct type  */
72      FILE  * fp;
73       int   c, prevc  =   ' \0 ' ;
74      
75       if  ( (fp  =  fopen(args -> fname,  " r " ))  !=  NULL ){
76           while ( ( c  =  getc(fp))  !=  EOF ){
77               if  (  ! isalnum(c)  &&  isalnum(prevc) )
78                  args -> count ++ ;
79              prevc  =  c;
80          }
81          fclose(fp);
82      }  else  
83          perror(args -> fname);
84      printf( " COUNT %d: waiting to get lock\n " , args -> tid);
85      pthread_mutex_lock( & lock );     /*  get the mailbox  */
86      printf( " COUNT %d: have lock, storing data\n " , args -> tid);
87       if  ( mailbox  !=  NULL ){
88          printf( " COUNT %d: oops..mailbox not empty. wait for signal\n " , args -> tid);
89          pthread_cond_wait( & flag, & lock );
90      }
91      printf( " COUNT %d:OK,I can write mail\n " , args -> tid);
92      mailbox  =  args;             /*  put ptr to our args there  */
93      printf( " COUNT %d: raising flag\n " , args -> tid);
94      pthread_cond_signal( & flag);     /*  raise the flag  */
95      printf( " COUNT %d: unlocking box\n " , args -> tid);
96      pthread_mutex_unlock( & lock );     /*  release the mailbox  */
97       return  NULL;
98  }

//运行结果

allen:~/study/unix_system/CH14$ ./twordcount4 inc.cc inc.cc inc.cc
MAIN: waiting for flag to go up
COUNT 1: waiting to get lock
COUNT 1: have lock, storing data
COUNT 1:OK,I can write mail
COUNT 1: raising flag
COUNT 1: unlocking box
COUNT 2: waiting to get lock
COUNT 2: have lock, storing data
COUNT 2: oops..mailbox not empty. wait for signal
COUNT 3: waiting to get lock
COUNT 3: have lock, storing data
COUNT 3: oops..mailbox not empty. wait for signal
MAIN: Wow! flag was raised, I have the lock
    105: inc.cc
Ok,I have read the mail
MAIN: waiting for flag to go up
COUNT 2:OK,I can write mail
COUNT 2: raising flag
COUNT 2: unlocking box
COUNT 3:OK,I can write mail
COUNT 3: raising flag
COUNT 3: unlocking box
MAIN: Wow! flag was raised, I have the lock
    105: inc.cc
Ok,I have read the mail
MAIN: waiting for flag to go up

 

出现死锁,按照上面的时序,出现count2 count3都在wait signal,子线程count2 wait

signal,pthread_cond_wait(&flag,  &lock)使得lock解锁了,这时count3也就进入了互斥

区,不应该出现这种情况。

然后主线程读完mail,singal,count2接到signal继续写mail,然后主线程wait signal,

注意这个时候count3和主线程都在wait signal,count2写完,signal

这时候唤醒的是count3…!count2的mail被丢了,而主进程还等着要读第

3封mail.

 

所以问题是主线程wait的signal 和 子线程wait 的signal不应该用同样的signal.

,使用semaphore 比较自然

一个子线程写,子线程通知主线程已写完,主线程读mailbox,主线程通知子线程已读完,另一子线程写....

写, 读, 写,读....

write = 0;

read = 0;

 

//server  main thread

 

v(read) //to let one client sub thread can write at first since mail == null  

for (i =0 ; i < sub threads num; i++)

p(write) 

read mail

mail = null

v(read)

 

//clinent  sub threads

p(read)

write mail

v(write)

   1 /* twordcount4.c - threaded word counter for two files.    

  2    *         - Version 4: condition variable allows counter
  3   *                            functions to report results early 
  4    */
  5 
  6  #include   < stdio.h >
  7  #include   < pthread.h >
  8  #include   < ctype.h >
  9  #include  < semaphore.h >
 10 
 11  struct  arg_set {         /*  two values in one arg */
 12           char   * fname;     /*  file to examine     */
 13           int   count;     /*  number of words     */
 14           int  tid;
 15  };
 16 
 17  struct  arg_set   * mailbox  =  NULL;
 18  static  sem_t sem_write;
 19  static  sem_t sem_read;
 20 
 21  main( int  ac,  char   * av[])
 22  {
 23      pthread_t      t1, t2, t3;         /*  two threads  */
 24       struct  arg_set args1, args2, args3;     /*  two argsets  */
 25       void             * count_words( void   * );
 26       int             reports_in  =   0 ;
 27       int            total_words  =   0 ;
 28 
 29       if  ( ac  !=   4  ){
 30          printf( " usage: %s file1 file2 file3\n " , av[ 0 ]);
 31          exit( 1 );
 32      }
 33 
 34       // init semaphore,first o means semaphore only available in this process,second mean init value 0
 35       if ( sem_init( & sem_write,  0 0 ==   - 1   ||
 36          sem_init( & sem_read,  0 0 ==   - 1 ) {
 37          printf( " Failed to init semaphore!\n " );
 38          exit( 1 );
 39      }
 40 
 41 
 42 
 43      args1.fname  =  av[ 1 ];
 44      args1.count  =   0 ;
 45      args1.tid  =   1 ;
 46      pthread_create( & t1, NULL, count_words, ( void   * & args1);
 47 
 48      args2.fname  =  av[ 2 ];
 49      args2.count  =   0 ;
 50      args2.tid  =   2 ;
 51      pthread_create( & t2, NULL, count_words, ( void   * & args2);
 52 
 53      args3.fname  =  av[ 3 ];
 54      args3.count  =   0 ;
 55      args3.tid  =   3 ;
 56      pthread_create( & t3, NULL, count_words, ( void   * & args3);
 57 
 58 
 59       sem_post( & sem_read);    // allow the first write
 60       while ( reports_in  <   3  ){
 61          printf( " MAIN: waiting for sub thread write\n " );
 62           sem_wait( & sem_write);
 63           // sleep(10);
 64          printf( " %7d: %s\n " , mailbox -> count, mailbox -> fname);
 65           total_words  +=  mailbox -> count;
 66           if  ( mailbox  ==   & args1) 
 67              pthread_join(t1,NULL);
 68           if  ( mailbox  ==   & args2) 
 69              pthread_join(t2,NULL);
 70           if  ( mailbox  ==   & args3) 
 71              pthread_join(t3,NULL);
 72           // sleep(10);
 73           mailbox  =  NULL;
 74          printf( " Ok,I have read the mail\n " );
 75           sem_post( & sem_read) ;  
 76          reports_in ++ ;
 77      }
 78      printf( " %7d: total words\n " , total_words);
          sem_destroy(&sem_read);
        sem_destory(&sem_write);
 79  }
 80  void   * count_words( void   * a)
 81  {
 82       struct  arg_set  * args  =  a;     /*  cast arg back to correct type  */
 83      FILE  * fp;
 84       int   c, prevc  =   ' \0 ' ;
 85      
 86       if  ( (fp  =  fopen(args -> fname,  " r " ))  !=  NULL ){
 87           while ( ( c  =  getc(fp))  !=  EOF ){
 88               if  (  ! isalnum(c)  &&  isalnum(prevc) )
 89                  args -> count ++ ;
 90              prevc  =  c;
 91          }
 92          fclose(fp);
 93      }  else  
 94          perror(args -> fname);
 95      printf( " COUNT %d: waiting for main thread read the mail\n " , args -> tid);
 96       sem_wait( & sem_read);
 97      printf( " COUNT %d:OK,I can write mail\n " , args -> tid);
 98       mailbox  =  args;              /*  put ptr to our args there  */
 99      printf( " COUNT %d: Finished writting\n " , args -> tid);
100       sem_post( & sem_write);
101       return  NULL;
102  }

也可以直接初始sem_read = 1从而不需要一开始sem_post(&sem_read)

//test

allen:~/study/unix_system/CH14$ ./twordcount4_semaphore inc.cc incprint.c empty.c 

COUNT 1: waiting for main thread read the mail

COUNT 2: waiting for main thread read the mail

COUNT 3: waiting for main thread read the mail

COUNT 1:OK,I can write mail

COUNT 1: Finished writting

MAIN: waiting for sub thread write

    105: inc.cc

Ok,I have read the mail

COUNT 2:OK,I can write mail

COUNT 2: Finished writting

MAIN: waiting for sub thread write

     76: incprint.c

Ok,I have read the mail

COUNT 3:OK,I can write mail

COUNT 3: Finished writting

MAIN: waiting for sub thread write

      0: empty.c

Ok,I have read the mail

    181: total words

allen:~/study/unix_system/CH14$ ./twordcount4_semaphore inc.cc inc.cc inc.cc

COUNT 1: waiting for main thread read the mail

COUNT 2: waiting for main thread read the mail

COUNT 3: waiting for main thread read the mail

COUNT 1:OK,I can write mail

COUNT 1: Finished writting

MAIN: waiting for sub thread write

    105: inc.cc

Ok,I have read the mail

COUNT 2:OK,I can write mail

COUNT 2: Finished writting

MAIN: waiting for sub thread write

    105: inc.cc

Ok,I have read the mail

MAIN: waiting for sub thread write

COUNT 3:OK,I can write mail

COUNT 3: Finished writting

    105: inc.cc

Ok,I have read the mail

    315: total words


 

 

 

 

你可能感兴趣的:(线程同步)