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

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, 00== -1 ||
 36        sem_init(&sem_read, 00== -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


 

 

 

原文地址:https://www.cnblogs.com/rocketfan/p/1530477.html