相互排斥锁通信机制
基本原理
相互排斥锁以排他方式防止共享数据被并发訪问,相互排斥锁是一个二元变量,状态为开(0)和关(1),将某个共享资源与某个相互排斥锁逻辑上绑定之后,对该资源的訪问操作例如以下:
(1)在訪问该资源之前须要首先申请相互排斥锁,假设锁处于开状态,则申请得到锁并马上上锁(关),防止其它进程訪问资源,假设锁处于关,则默认堵塞等待.
(2)仅仅有锁定该相互排斥锁的进程才干释放该相互排斥锁.
相互排斥量类型声明为pthread_mutex_t数据类型,在<bits/pthreadtypes.h>中有详细的定义。
相互排斥量初始化和销毁
在使用相互排斥锁之前须要定义相互排斥锁(全局变量),定义代码:
pthread_mutex lock;
/* Initialize a mutex. */ int pthread_mutex_init (pthread_mutex_t *__mutex, // 指向要初始化的相互排斥锁的指针 __const pthread_mutexattr_t *__mutexattr); // 指向相互排斥锁属性对象的指针,设null为默认属性 /* Destroy a mutex. */ int pthread_mutex_destroy (pthread_mutex_t *__mutex);
上面两个函数分别因为相互排斥量的初始化和销毁。
假设相互排斥量是静态分配的,能够通过常量进行初始化,例如以下:
#define PTHREAD_MUTEX_INITIALIZER {{0, }} // 系统定义的,无需声明 pthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER;
当然也能够通过pthread_mutex_init()进行初始化。对于动态分配的相互排斥量因为不能直接赋值进行初始化就仅仅能採用这样的方式进行初始化,pthread_mutex_init()的第二个參数是相互排斥量的属性,假设採用默认的属性设置,能够传入NULL。
当不在须要使用相互排斥量时,须要调用pthread_mutex_destroy()销毁相互排斥量所占用的资源。
相互排斥量的属性设置
/* 初始化相互排斥量属性对象 */ int pthread_mutexattr_init (pthread_mutexattr_t *__attr); /* 销毁相互排斥量属性对象 */ int pthread_mutexattr_destroy (pthread_mutexattr_t *__attr); /* 获取相互排斥量属性对象在进程间共享与否的标志 */ int pthread_mutexattr_getpshared (__const pthread_mutexattr_t *__restrict __attr, int *__restrict __pshared); /* 设置相互排斥量属性对象,标识在进程间共享与否 */ int pthread_mutexattr_setpshared (pthread_mutexattr_t *__attr, int __pshared);
相互排斥量在初始化的时候pthread_mutex_init的第二个參数是相互排斥量的属性,假设为NULL空指针,那么就使用默认属性。
相互排斥量属性的数据类型为pthread_mutexattr_t,它的初始化和销毁和相互排斥量类似。一旦相互排斥量属性对象被初始化后,就能够通过调用不同的函数启用或禁止特定的属性。这里列出了一个设置特定属性的函数:pthread_mutexattr_setpshared,能够用于指定相互排斥量在不同进程间共享,这样能够通过相互排斥量来同步不同的进程,当然前提是该相互排斥量位于进程间的共享内存区。
pthread_mutexattr_setpshared()函数的第二个參数__pshared用于设定是否进程间共享,其值能够是PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED,后者是设置进程间共享。
以下是使相互排斥量能够在进程间共享的大致过程:
pthread_mutex_t *pSharedMutex; //指向共享内存区的相互排斥量 pthread_mutexattr_t mutexAttr; //相互排斥量属性 pSharedMutex = /*一个指向共享内存区的指针*/; pthread_mutexattr_init(&mutexAttr); pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED); pthread_mutex_init(pSharedMutex, &mutexAttr);
相互排斥量的申请使用
/* Try locking a mutex. */ int pthread_mutex_trylock (pthread_mutex_t *__mutex); /* Lock a mutex. */ int pthread_mutex_lock (pthread_mutex_t *__mutex); /* Unlock a mutex. */ int pthread_mutex_unlock (pthread_mutex_t *__mutex);这几个函数都非常easy,通过pthread_mutex_lock()函数获得訪问共享资源的权限,假设已经有其它线程锁住相互排斥量,那么该函数会是线程堵塞指定该相互排斥量解锁为止。 pthread_mutex_trylock()是相应的非堵塞函数,假设相互排斥量已被占用,它会返回一个EBUSY错误。訪问完共享资源后,一定要通过pthread_mutex_unlock() 函数,释放占用的相互排斥量。同意其它线程訪问该资源。
这里要强调的是:相互排斥量是用于上锁的,不能用于等待。
简单说就是,相互排斥量的使用流程应该是:线程占用相互排斥量,然后訪问共享资源,最后释放相互排斥量。而不应该是:线程占用相互排斥量,然后推断资源是否可用,假设不可用,释放相互排斥量,然后反复上述过程。这样的行为称为轮转或轮询,是一种浪费CPU时间的行为。
应用演示样例
以下的程序使用了两个同进程的线程,一个线程负责从标准设备读入数据存储在共享数据区,还有一个负责将读入的数据输出到标准设备.事实上是一个生产者消费者的变形.
- 处理输入操作的线程在接受用户信息的时候必须相互排斥使用该资源,不能被打断,堵塞其它线程的进行
- 输出线程也不能被打断,须要先锁定相互排斥锁,操作完毕后释放,
- 程序结束,销毁相互排斥锁.
代码例如以下:
/************************************************************************* > File Name: pthread_exp.c > Author:SuooL > Mail:1020935219@qq.com || hu1020935219@gmail.com > Website:http://blog.csdn.net/suool | http://suool.net > Created Time: 2014年08月15日 星期三 08时42分45秒 > Description: ************************************************************************/ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include <string.h> void *thread_function(void *arg); pthread_mutex_t work_mutex; // 定义全局相互排斥锁对象 #define WORK_SIZE 1024 char work_area[WORK_SIZE]; // 全局共享数据区 int time_to_exit = 0; // 标志变量 // main函数 int main(int argc,char *argv[]) { int res; pthread_t a_thread; // 线程 void *thread_result; res = pthread_mutex_init(&work_mutex, NULL); //init mutex 初始化相互排斥锁 if (res != 0) { perror("Mutex initialization failed"); exit(EXIT_FAILURE); } res = pthread_create(&a_thread, NULL, thread_function, NULL);//create new thread if (res != 0) { perror("Thread creation failed"); exit(EXIT_FAILURE); } pthread_mutex_lock(&work_mutex); //lock the mutex,before get the input printf("Input some text. Enter 'end' to finish "); while(!time_to_exit) { fgets(work_area, WORK_SIZE, stdin); //get a string from stdin pthread_mutex_unlock(&work_mutex); //unlock the mutex while(1) { pthread_mutex_lock(&work_mutex); //lock the mutex if (work_area[0] != '