嵌入式 互斥锁和条件变量基础常识

前言

在学习posixipc编程的时候,遇到两个很重要的概念:互斥锁和条件变量,而且,在各种进程/线程之间的同步中的使用很多。所以今天就系统的整理了一下,主要的参考资料来自《UNIX网络编程卷2:进程间通信》

一、概述

在多线程或者多进程共享数据时,为了保证数据的完整性和正确性,,使用同步机制来实现。互斥锁和条件变量均是出自posix.1线程标准,可以被用来同步一个进程中的多个线程,还可以用来同步共享一块内存空间的多个进程。

比如典型的,在多处理线程编程生产者消-费者问题的时候,最值得注意的就是那些被共享的数据区,因为无法知道在哪个时候哪个线程在使用着这些共享数据,也无法预知哪个线程会先运行它,哪个线程后运行它,所以对这些资源做合理的分配和正确的使用是非常有必要的。在Linux中,提供了互斥锁、条件变量以及信号量来对共享资源进行保护。关于信号量在本篇中暂不做说明,只讨论互斥锁和条件变量在同步中的应用。

二、互斥锁

互斥锁(英文:Mutualexclusion,常缩写为Mutex)是一种常用在多线程编程中,防止多个线程对一个公共资源做读写操作的机制,以保证共享操作的数据的完整性。互斥锁也可以从字面意思来看,就是相互排斥的锁,它是最基本的进程或者线程间同步的方法,用来保护临界区,以保证任何时候只有一个线程或者进程在访问共享资源(如共享的代码段)。保护一个临界区的代码形式大致如下:

lock_the_mutex(...)

临界区

unlock_the_mutex(...)

posix的互斥锁被声明为pthread_mutex_t类型的变量。互斥锁也分为静态互斥锁和动态互斥锁两类,如果是静态互斥锁,常常会把它初始化为一个常值PTHREAD_MUTEX_INITIALIZER,就像这样:

static pthread_mutex_t lock =PTHREAD_MUTEX_INITIALIZER;

在内核中关于阻塞互斥锁的具体实现可以查看:include/linux/mutex.h这个文件。

如果互斥锁是动态分配的,比如通过malloc来分配的或者是分配在共享内存中,那么真正使用它之前一定要用pthread_mutex_init函数来对它做初始化。下面的三个函数是对互斥锁进行上锁和解锁:

#include<pthread.h>

int pthread_mutex_lock(pthread_mutex_t*mptr);

int pthread_mutex_trylock(pthread_mutex_t*mptr);

int pthread_mutex_unlock(pthread_mutex_t*mptr);

对以上三个函数,如果调用成功均返回0,失败返回相应的errno值。

互斥锁还有协作性,就是说,假如共享数据是一个链表,那么对该链表进行操作的所有线程都必须在其执行实际操作之前获取到该互斥锁。

三、互斥锁实现的生产者-消费者模型

以多生产者,单消费者的模型实践了互斥锁的作用。

问题描述:一个或者多个生产者(线程或者进程)创建着一个个的数据条目,然后这些条目由一个消费者(线程或者进程)处理。

实现方式:

1、只考虑多个生产者之间的同步互斥(producer_consumer1.c)

https://github.com/helianthuslulu/LINUX_IPC

2、生产者和消费者之间的同步,消费者采用轮询方式(producer_consumer2.c)

https://github.com/helianthuslulu/LINUX_IPC

四、条件变量

互斥锁的确能很好的实现进程/线程之间的同步问题,但是它是通过锁机制来实现的,就是仅仅通过加锁和解锁实现同步,效率比较低,于是就有了条件变量(ConditionVariable),条件变量允许一个进程或者是线程睡眠直到某个事件为止。

互斥锁用于上锁,而条件变量用于等待。条件变量是类型为pthread_cond_t的变量。具体来说条件变量可以适用的情况:在线程同步中存在这样的一种情况,某个线程A需要等待某个条件成立之后才能继续往下执行,如果当前这个条件不成立,那么该线程就阻塞等待,而如果某个时刻另一个线程B在执行的过程中使得这个条件成立了,那么就会唤醒相称A继续往下执行。

pthread库中用条件变量来阻塞等待一个条件,或者唤醒等待这个条件的进程。

条件变量操作相关的函数:

#include<pthread.h>

int pthread_cond_wait(pthread_cond_t*cond,pthread_mutex_t*mutex);

int int pthread_cond_signal(pthread_cond_t*cond);

int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t*attr);

int pthread_cond_destroy(pthread_cond_t*cond);

int pthread_cond_timedwait(pthread_cond_t*cond,pthread_mutex_t mytex,const struct timespec*abstime);

int pthread_cond_broadcast(pthread_cond_t*cond);

 

pthread_cond_wait函数是用于条件变量的等待;

pthread_cond_signal用于条件变量的通知;

pthread_cond_init用户条件变量初始化;

pthread_cond_destroy用于条件变量的摧毁;

pthread_cond_timedwait也是用于条件变量的等待,不过它与pthread_cond_wait的区别在于,在pthread_cond_timewait中,如果等待时间达到或是超过所引用的参数*abstime,它将结束并返回错误ETIME

不过这里的“条件”可以是我们编程者自己选择,然后会在具体的代码实现中测试这种条件。

条件变量是利用线程间共享的全局变量来进行同步的一种机制,它的实现主要有两个动作:

(1)一个线程等待条件变量的成立而被挂起;

(2)由另外某个线程使得条件变量成立,给出条件成立的信号,唤醒该睡眠线程。

条件变量中条件的检测是在互斥锁的保护下进行的,所以说每个条件变量总是会有一个互斥锁与之关联。

在具体使用条件变量之前也是要进行初始化的,可以在单条语句中生成并且初始化一个条件变量,像这样:pthread_cond_t my_condition =PTHREAD_COND_INITIALIZER;

五、使生产者消费者模型来解释条件变量的使用

还是第三部分所提到的问题,不过使用了条件变量来通知消息,而不必在总是让消费者轮询,对之前的代码再次做了改进:

producer_consumer3.c

https://github.com/helianthuslulu/LINUX_IPC

你可能感兴趣的:(嵌入式 互斥锁和条件变量基础常识)