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


    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


     

     

     

  • 相关阅读:
    PHP fread() 函数
    PHP fputs() 函数
    PHP fputcsv() 函数
    PHP fpassthru() 函数
    分区表基本类型
    C# const 和 readonly 有什么区别
    win2d 画出好看的图形
    win2d 画出好看的图形
    win2d 渐变颜色
    win2d 渐变颜色
  • 原文地址:https://www.cnblogs.com/rocketfan/p/1530477.html
Copyright © 2020-2023  润新知