为什么一个条件变量要用互斥变量来保护

========================

======================== 内的内容为网上的分析


==============================================begin

现在来看一段典型的应用:看注释即可。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

#include<pthread.h>

#include<unistd.h>

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

 

static pthread_mutex_t mtx=PTHREAD_MUTEX_INITIALIZER;

static pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

 

struct node {

    int n_number;

    struct node *n_next;

} *head=NULL; /*[thread_func]*/

 

/*释放节点内存*/

static void cleanup_handler(void*arg) {

    printf("Clean up handler of second thread.\n");

    free(arg);

    (void)pthread_mutex_unlock(&mtx);

}

 

static void *thread_func(void *arg) {

    struct node*p=NULL;

    pthread_cleanup_push(cleanup_handler,p);

 

    while(1) {

        pthread_mutex_lock(&mtx);

        //这个mutex_lock主要是用来保护wait等待临界时期的情况,

        //当在wait为放入队列时,这时,已经存在Head条件等待激活

        //的条件,此时可能会漏掉这种处理

        //这个while要特别说明一下,单个pthread_cond_wait功能很完善,

        //为何这里要有一个while(head==NULL)呢?因为pthread_cond_wait

        //里的线程可能会被意外唤醒,如果这个时候head==NULL

        //则不是我们想要的情况。这个时候,

        //应该让线程继续进入pthread_cond_wait

 

        while(1) {

            while(head==NULL) {

                pthread_cond_wait(&cond,&mtx);

            }

            //pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,

            //然后阻塞在等待队列里休眠,直到再次被唤醒

            //(大多数情况下是等待的条件成立而被唤醒,唤醒后,

            //该进程会先锁定先pthread_mutex_lock(&mtx);,

            //再读取资源用这个流程是比较清楚的

            /*block-->unlock-->wait()return-->lock*/

 

            p=head;

            head=head->n_next;

            printf("Got%dfromfrontofqueue\n",p->n_number);

            free(p);

        }

        pthread_mutex_unlock(&mtx);//临界区数据操作完毕,释放互斥锁

    }

 

    pthread_cleanup_pop(0);

    return 0;

}

 

int main(void) {

    pthread_t tid;

    int i;

    struct node *p;

    pthread_create(&tid,NULL,thread_func,NULL);

    //子线程会一直等待资源,类似生产者和消费者,

    //但是这里的消费者可以是多个消费者,

    //而不仅仅支持普通的单个消费者,这个模型虽然简单,

    //但是很强大

    for(i=0;i<10;i++) {

        p=(struct node*)malloc(sizeof(struct node));

        p->n_number=i;

        pthread_mutex_lock(&mtx);//需要操作head这个临界资源,先加锁,

        p->n_next=head;

        head=p;

        pthread_cond_signal(&cond);

        pthread_mutex_unlock(&mtx);//解锁

        sleep(1);

    }

    printf("thread1wannaendthecancelthread2.\n");

    pthread_cancel(tid);

    //关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,

    //子线程会在最近的取消点,退出线程,而在我们的代码里,最近的

    //取消点肯定就是pthread_cond_wait()了。

    pthread_join(tid,NULL);

    printf("Alldone--exiting\n");

    return 0;

}


==============================================end

解释

1. 根据《Unix环境高级编程》p335 11.6.6 上述程序的77行应与76行交换一下。紫等待线程发信号时,不需要占有互斥量。即:线程1  锁住->等条件 , 线程2  创造条件->解锁->发信号。

2.wait时的时间窗口。类似的情况如《Unix环境高级编程》p321 ,需要确保对象在释放之前不会被找到。


 ============begin

现在这里有个问题,一定要在发送通知之前解锁吗?答案是肯定的,为什么,因为如果先发送通知信号给线程1的时候,pthread_cond_wait可能在线程2的解锁之前就返回,而当它返回的时候,会再次将这个所进行锁定,而这个所还没有在线程2中解锁,应次会使其在次卡住。虽然这个卡住在线程2运行到解锁处会消除,但这并不符合我们有时的需求,所以最好还是在解锁之后在发送信号。

 pthread_mutex_lock(&qlock);

 pthread_mutex_unlock(&qlock);

  pthread_cond_signal(&qready);

 

 

函数pthread_cond_wait把自己放到等待条件的线程列表上,然后对互斥锁解锁(这两个操作是原子操作)。在函数返回时,互斥量再次被锁住

==================end


===================begin

为什么存在条件变量

      首先,举个例子:在应用程序中有连个线程thread1thread2thread3thread4,有一个int类型的全局变量iCountiCount初始化为0thread1thread2的功能是对iCount的加1thread3的功能是对iCount的值减1,而thread4的功能是当iCount的值大于等于100时,打印提示信息并重置iCount=0

      如果使用互斥量,线程代码大概应是下面的样子:

      thread1/2

      while (1)

      {

            pthread_mutex_lock(&mutex);

            iCount++;

            pthread_mutex_unlock(&mutex);

      }

      thread4:

      while(1)

      {

            pthead_mutex_lock(&mutex);

            if (100 <= iCount)

            {

                 printf("iCount >= 100\r\n");

                  iCount = 0;

                 pthread_mutex_unlock(&mutex);

            }

            else

            {

                 pthread_mutex_unlock(&mutex);

            }

      }

      在上面代码中由于thread4并不知道什么时候iCount会大于等于100,所以就会一直在循环判断,但是每次判断都要加锁、解锁(即使本次并没有修改iCount)。这就带来了问题一,CPU浪费严重。所以在代码中添加了sleep(),这样让每次判断都休眠一定时间。但这由带来的第二个问题,如果sleep()的时间比较长,导致thread4处理不够及时,等iCount到了很大的值时才重置。对于上面的两个问题,可以使用条件变量来解决。

     首先看一下使用条件变量后,线程代码大概的样子:

     thread1/2:

      while(1)

      {

              pthread_mutex_lock(&mutex);

              iCount++;

             pthread_mutex_unlock(&mutex);

              if (iCount >= 100)

              {

                    pthread_cond_signal(&cond);

              }

      }         

      thread4:

      while (1)

      {

             pthread_mutex_lock(&mutex);

             while(iCount < 100)

             {

                   pthread_cond_wait(&cond, &mutex);

             }

             printf("iCount >=100\r\n");

             iCount = 0;

             pthread_mutex_unlock(&mutex);

      }

      从上面的代码可以看出thread4中,当iCount < 100时,会调用pthread_cond_wait。而pthread_cond_wait在上面应经讲到它会释放mutex,然后等待条件变为真返回。当返回时会再次锁住mutex。因为pthread_cond_wait会等待,从而不用一直的轮询,减少CPU的浪费。在thread1thread2中的函数pthread_cond_signal会唤醒等待cond的线程(即thread4),这样当iCount一到大于等于100就会去唤醒thread4。从而不致出现iCount很大了,thread4才去处理。

     需要注意的一点是在thread4中使用的while (iCount < 100),而不是if (iCount < 100)。这是因为在pthread_cond_singal()pthread_cond_wait()返回之间有时间差,假如在时间差内,thread3又将iCount减到了100以下了,那么thread4就需要在等待条件为真了。

===================end


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值