linux下的同步与互斥

    谈到linux的并发,必然涉及到线程之间的同步和互斥,linux主要为我们提供了几种实现线程间同步互斥的
机制,本文主要介绍互斥锁,条件变量和信号量。互斥锁和条件变量包含在pthread线程库中,使用时需要包含
<pthread.h>头文件。而使用信号量时需要包含<semaphore.h>头文件。
1.互斥锁
    类型声明:pthread_mutex_t mutex;
    对互斥量的初始化:
        程序在使用pthread_mutex_t之前需要先对其进行初始化,对于静态分配的pthread_mutex_t变量来说,
        只要将PTHREAD_MUTEX_INITIALIZER赋给变量就行了,语句如下:
            static pthread_mutex_t mutex=PTHREAD_mutex_INITIALIZER;
        而对于动态分配或没有默认互斥属性的互斥变量来说,要调用pthread_mutex_init函数来执行初始化工
        作。函数声明如下:
            int pthread_mutex_init(pthread_mutex_t* mutex,const pthread_mutexattr_t* attr);
        mutex是指向一个互斥量的指针,attr是指向一个属性结构体的指针,为NULL时使用默认属性。
        需要注意的是pthread_mutex_init函数只能恰好执行一次,重复执行的结果是未定义的。
    对互斥量的操作:
        int pthread_mutex_lock(pthread_mutex_t* mutex);//对互斥量执行锁定操作
        int pthread_mutex_unlock(pthread_mutex_t* mutex);//对互斥量执行解锁操作
    对互斥量的销毁:
        int pthread_mutex_destroy(pthread_mutex_t* mutex);//
    使用方法:
        以下代码用互斥量来保护一个临界区:
        pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//初始化
        pthread_mutex_lock(&mutex);//获得锁
        /*critical section code*/
        pthread_mutex_unlock(&mutex);//释放锁
    通常比较适用的方法是将编写一组访问临界区的函数,将锁定调用都放在这些函数中,这是确保对对象的拍他
    性访问的一种方法,这样锁定机制对调用线程就是透明的了。
    
2.条件变量
    有时,我们可能需要让线程在某个条件满足之前一直等待,在未使用条件变量之前,您可能会使用忙等待,如
    下列代码:
        while(x!=y);
    这样的方式,然而这种占着茅坑不拉屎的行为是非常不被提倡的。因此,我们引入条件变量的概念来是线程在
    某个条件不满足是进入挂起状态。
    类型声明:pthread_cond_t cond;
    对条件变量的初始化:
        程序在使用pthread_cond_t变量之前必须对其进行初始化。对于静态分配的pthread_cond_t变量来说,
        只要将PTHREAD_COND_INITIALIZER赋给变量就星了,语句如下:
        pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
    对于动态分配的或没有默认属性的变量来说,就要调用pthread_cond_init函数来执行初始化。函数声明如下:
        int pthread_cond_init(pthread_cond_t* cond,const pthread_cond_attr_t* attr);
        cond是指向一个条件变量的指针,attr是指向一个属性结构体的指针,为NULL时使用默认属性。
    对条件变量的操作:
        int pthread_cond_wait(pthread_cond_* cond,pthread_mutex_t* mutex);//使线程阻塞于某个条件。
        int pthread_cond_signal(pthread_cond_t* cond);//唤醒某个阻塞在cond上的线程。
    对条件变量的销毁:
        int pthread_cond_destroy(pthread_cond_t* cond);
    使用方法:
        条件变量是与断言或条件的测试一同调用的,以此来实现将条件变量关联到某个断言上去。通常线程会对
        一个条件进行测试,如果失败,就会调用pthread_cond_wait函数将线程阻塞。示例代码如下:
            pthread_mutex_lock(&mutex);
            while( a<b )
                pthread_cond_wait(&cond,&mutex);
            pthread_mutex_unlock(&mutex);
        调用线程应该在它测试或调用pthread_cond_wait之前获得一个互斥量,以避免在测试条件的时候有其他
        线程改写条件中变量的值。pthread_cond_wait函数中第二个参数是一个互斥量类型的指针,线程在调用
        pthread_cond_wait后会隐式的原子地释放mutex互斥量并阻塞,允许其他线程获得互斥量并修改断言中
        的变量。当线程成功的从pthread_cond_wait中返回时,它就再次拥有了互斥量,并且不用显式的重新获
        得互斥量。
        当其他线程修改了断言中变量的值后可以调用pthread_cond_signal函数来唤醒一个等待在某个断言成真
        的线程。也可以使用pthread_cond_broadcast(pthread_cond_t*)函数来唤醒所有等待在某个条件变量
        上的线程。在修改断言中出现的任一变量之前要获得互斥量。示例代码如下:
            pthread_mutex_lock(&mutex);
            a++;
            pthread_cond_signal(&cond);//pthread_cond_broadcast(&cond);
            pthread_mutex_unlock(&mutex);
            
3.信号量
    信号量是一个整型变量,它带有两个原子操作wait和signal。wait操作还可以被称为down、P操作。signal操
    作还可以被称为up、V、post操作。
    如果S大于0,wait操作就在一个原子操作中对其进行减量运算。如果S等于0,wait操作就就在一个原子操作中
    测试S,阻塞调用程序,将调用程序放入wait的等待队列中。
    如果有线程在信号量上阻塞,则S必然等于0,signal操作就会解除对某一个等待线程的阻塞。如果S大于0,即
    没有线程阻塞在信号量上,signal就对S进行增量操作。
    可以把信号量理解为临界区中资源的可用数量,wait表示对资源的申请,当没有可用资源时信号量为0,signal
    表示线程使用资源后,对资源的释放。
    下面介绍几种通过信号量来控制线程按某种顺序执行的方法:
    1.线程1中a先于线程2中b执行[S初始话为0]
        Process 1:
            a;
            signal(&S);
        Process 2:
            wait(&S);
            b;
    2.线程1中a于线程2中b语句交替执行[S,Q初始化为1]
        Process 1:
            while(1){
                wait(&S);
                a;
                signal(&Q);
            }
        Process 2:
            while(1){
                wait(&Q);
                b;
                signal(&S);
            }
    可通过让S为0Q为1,或让S为1Q为0,来保证让a或b先执行。
    需要注意的是,为了避免申请多个资源发生死锁,应按照相同的顺序申请资源,这里公司面试的时候经常会被
    问到。
    
    
    linux中信号量有无名信号量和命名信号量之分,无名信号量可用于线程之间的同步和互斥,命名信号量可用
    于进程间的通信,命名信号量与命名管道相似,以文件的形式存储于磁盘上。
    
  无名信号量:
    类型声明:
        sem_t sem;
    初始化:
        int sem_init(sem_t* sem,int pshared,int value);
        参数pshared为0,表示信号量只能由初始化这个信号量的进程中的线程使用。
        value表示要将sem初始化的值。value不能为负。
    操作:
        int sem_post(sem_t* sem);//signal操作
        int sem_wait(sem_t* sem);//wait操作
    销毁:
        int sem_destroy(sem_t* sem);
    使用方法:
        通常先由主线程调用sem_init对信号量进行初始化。然后在其他线程中调用post或wait函数。示例代码
        如下:
            static sem_t sem;
            sem_init(&sem,0,1);
            sem_wait(&sem);
            /*critical section*/
            sem_post(&sem);
        
  命名信号量:
    类型声明:
        sem_t sem;
    初始化:
        sem_t* sem_open(const char* name,int oflag,...);
        参数oflag用来确定是创建信号量,还是仅仅由函数对其访问。若oflag中的O_CREAT比特位被设置,则需
        要另外两个参数,mode_t mode为文件权限,unsigned value为信号量值。
    关闭:
        int sem_close(sem_t* sem);
    删除:
        int sem_unlink(sem_t* sem);
    操作方式与无名信号量相同。

转载于:https://my.oschina.net/nalenwind/blog/118339

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值