线程同步互斥

当解决多线程互斥同步的问题时,经常会有如下几个问题:

1. 在一个给定的问题中,需要多少个Mutex,多少个Semaphore?有什么规律?
2. 在对临界区加锁和等待信号量的顺序上有什么要求和规律?
3. 什么样操作适合放在临界区,什么样的不适合?

下面就生产者和消费者问题来分析一些这几个问题.
下面是一个简单的实现程序:
生产者向数组sharedArray中写入数据,而消费者从该数组中读取数据.

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>

#define MAXSIZE  5               /*共享缓冲区的大小*/

int sharedArray[MAXSIZE];        /*sharedArray是共享缓冲区*/
int curr=-1;                     /*curr是用来指定sharedArray当前存有数据的最大位置*/
                                 /*注意,sharedArray和curr都属于共享数据*/

int empty=0;            
int full=MAXSIZE;
pthread_mutex_t sharedMutex=PTHREAD_MUTEX_INITIALIZER; /*锁定临界区的mutex*/
sem_t waitNonEmpty, waitNonFull; /*等待"非空资源"和等待"非满资源"的semaphor*/

void * readData(void * whichone)
{
        int data, position;
        while (1){
                sem_wait(&waitNonEmpty);             /*是否有"非空资源"*/

                pthread_mutex_lock(&sharedMutex);    /*进入临界区*/
                data = sharedArray[curr];
                position = curr--;
                printf ("%s read from the %dth: %d, /n", (char*)whichone, position, data);
                sem_post(&waitNonFull);              /*生成一个"非满资源"*/
                pthread_mutex_unlock(&sharedMutex);  /*离开临界区*/

                sleep(2);                            /*跟同步无关的费时操作*/
        }
}

void * writeData(void * whichone)
{
        int data, position;
        while (1) {
                data=(int)(10.0*random()/RAND_MAX);    /*生成一个随机数据,注意是10.0而不是10*/
                sem_wait(&waitNonFull);                /*是否有"非满资源"*/

                pthread_mutex_lock(&sharedMutex);      /*进入临界区*/
                position = ++curr;
                sharedArray[curr]=data;
                printf ("%s wrote to the %dth: %d, /n", (char*)whichone, position, data);
                sem_post(&waitNonEmpty);               /*生成一个"非空资源"*/
                pthread_mutex_unlock(&sharedMutex);    /*离开临界区*/

                sleep(1);                              /*跟同步无关的费时操作*/

        }
}


int main (int argc, char** argv)
{
        pthread_t consumer1, consumer2, producer1, producer2;    /*两个生产者和两个消费者*/
        sem_init(&waitNonEmpty, 0, empty);                       /*初始化信号量*/
        sem_init(&waitNonFull, 0, full);            
        /*注意,本问题中的两种semaphore是有一定关系的,那就是它们的初始值之和应该等于共享缓冲区大小*/
        /*即empty+full等于MAXSIZE*/

        pthread_create (&consumer1, NULL, &readData, "consumer1");
        pthread_create (&consumer2, NULL, &readData, "consumer2");
        pthread_create (&producer1, NULL, &writeData, "producer1");
        pthread_create (&producer2, NULL, &writeData, "producer2");
        pthread_join (consumer1, NULL);
        pthread_join (consumer2, NULL);
        pthread_join (producer1, NULL);
        pthread_join (producer2, NULL);
        sem_destroy(&waitNonEmpty);
        sem_destroy(&waitNonFull);

}


分析和说明:

1. 在一个给定的问题中,需要多少个Mutex,多少个Semaphore?有什么规律?

在本问题中,共需要一个Mutex和两个Semaphore.
其中,Mutex是用来锁定临界区的,以解决对共享数据的互斥访问问题(无论是对生成者还是对消费者);
我们共需要两个Semaphore,这是因为在本问题中共有两个稀缺资源.
第一种是"非空"这种资源,是在消费者之间进行竞争的.
第二种是"非满"这种资源,是在生产者之间进行竞争的.
所以,一般来说,需要锁定临界区,就需要Mutex;有几种稀缺资源就需要几个Semaphore.
对稀缺资源的分析不能想当然.稀缺资源不一定是指被共享的资源,很多时候是指线程会被阻塞的条件(除了要进临界区被阻塞外).
本例中,消费者会在缓冲区为空时被阻塞,所以"非空"是一种稀缺资源;
生产者会在缓冲区为满时被阻塞,所以"非满"也是一种稀缺资源.

2. 在对临界区加锁和等待信号量的顺序上有什么要求和规律?

这里要说两点:
第一,不要将等待信号量的语句放在被锁定的临界区内,这样会造成死锁.而且这也是很没有必要的.
比如,消费者在缓冲区没有数据的时候进入临界区,这样就会把临界区锁上,由于没有数据,消费者也会被锁上.
这时,任何生产者都会由于临界区被锁上而被block住,这样就造成了死锁.
第二,如果有多个Semaphore需要等待,那么每个线程中,最好对这多个信号量进行等待的顺序一致,
不然的话很容易造成死锁.

3.  什么样操作适合放在临界区,什么样的不适合?

一般来说,临界区中只放对共享数据进行访问的语句,这样会改善程序的性能.
很多时候,取出共享数据的副本后,对副本进行费时的各种操作就不需要放在临界区了.
比如,本例中的sleep语句就根本不需要放入临界区.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值