Linux进程通信[2]-互斥锁和条件变量

来源:互联网 发布:徐老师的淘宝店网址 编辑:程序博客网 时间:2024/06/06 00:47

Linux进程通信[2]-互斥锁和条件变量

概述

上一篇介绍了共享内存,已经属于比较高级的层次。本篇介绍一下多线程/多进程最基础的问题,同步。
为了允许在线程或进程间共享数据,同步常常是必需的,也就是我们常说要用锁(当然锁通常也是性能瓶颈,现在无锁架构正在越发流行)。互斥锁和条件变量则是同步的基本组成部分。互斥锁和条件变量在同一进程下的所有线程内是共享的,所有它天然可以用于线程同步。如果将互斥锁和条件变量存放在多个进程的共享区内,则同样可以用于进程间同步。(Posix标准)

互斥锁

互斥锁用于保护临界区(critical section),保证在任何时刻只有一个线程/进程在执行其中的代码。保护一个临界区的代码通常轮廓如下:

1lock_the_mutex(...);
2//do anything one by one
3unlock_the_mutex(...);

在实际代码中,我们通常使用如下的数据机构和函数。

01#include <pthread.h>
02//pthread_mutex_t 是锁的定义
03static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZE;
04 
05//尝试对ptr上锁,否则一直阻塞
06int pthread_mutex_lock(pthread_mutex_t *ptr);
07//尝试对ptr上锁,失败则直接返回
08int pthread_mutex_trylock(pthread_mutex_t *ptr);
09//解锁
10int pthread_mutex_unlock(pthread_mutex_t *ptr);

条件变量

条件变量(condition)不同于互斥锁,它主要用于等待。因为我们在线程/进程同步时,通常都需要等待其他线程/进程完成任务再继续自己的任务。这时,仅仅使用mutex就无法完美的解决这个问题。假设我们只使用mutex,则需要锁上一个mutex然后查询,如果没有前序任务则解锁,然后隔断时间再继续上述过程。而借助条件变量,我们则可以大大简化这个过程。在锁上mutex后,如果没有前序任务则调用wait函数,系统会自动释放mutex并且等待前置信号的到达,且信号到达后还是拥有mutex。
下面是condition的常用函数:

1#include <pthread.h>
2//pthread_mutex_t 是条件变量的定义
3static pthread_cond_t lock = PTHREAD_COND_INITIALIZER;
4 
5//等待条件变量 cptr
6int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr);
7//发送条件变量 cptr
8int pthread_cond_signal(pthread_cond_t *cptr);

每个条件变量总有一个互斥锁与之关联。(个人猜测原因:因为我们等待条件变量时候,需要释放mutex,交给其他线程

样例

这里用一个简单的生产者-消费者模型来演示如何使用mutex和condition。

01#include <pthread.h>
02 
03pthread_mutex_t p_mutex = PTHREAD_MUTEX_INITIALIZER;
04pthread_mutex_t c_mutex = PTHREAD_MUTEX_INITIALIZER;
05pthread_cond_t c_cond = PTHREAD_COND_INITIALIZER;
06 
07int LEN = 100;
08int arr[LEN];
09int nready = 0;
10 
11void *produce(void *arg) {
12    int i = 0;
13    for (;;) {
14        //多线程生产
15        pthread_mutex_lock(&p_mutex);
16        if (i > LEN) {
17            pthread_mutex_unlock(&p_mutex);
18            break;
19        }
20        arr[i++] = i;
21        pthread_mutex_unlock(&p_mutex);
22 
23        //需要和消费进程 同步 完成数
24        pthread_mutex_lock(&c_mutex);
25        if (nready == 0) {
26            //通知消费进程
27            pthread_cond_signal(&c_cond);
28        }
29        nready++;
30        pthread_mutex_unlock(&c_mutex);
31    }
32}
33 
34void *consume(void *arg) {
35    //单线程消费
36    for (int i = 0; i < LEN; ++i) {
37        pthread_mutex_lock(&c_mutex);
38        if (nready == 0) {
39            //等待消费者通知
40            pthread_cond_wait(&c_cond, &c_mutex);
41        }
42        nready--;
43        pthread_mutex_unlock(&c_mutex);
44 
45        //do something for arr[i];
46    }
47}
原创粉丝点击