线程之间的同步与互斥
由于线程共享进程的资源和地址空间,因此在对这些资源进行操作时,必须考虑到线程间资源访问的同步和互斥问题。以下主要介绍POSIX中两种线程同步机制,分别为互斥锁和信号量。这两个同步机制可以通过互相调用对方来实现,单互斥锁更适用于同时可用的资源是唯一的情况;信号量更适用于同时可用的资源为多个的情况。
1. 互斥锁线程控制
1.1 互斥锁编程说明
互斥锁是用一种简单的加锁方式来控制对共享资源的原子操作。互斥锁只有两种状态,即上锁和解锁,可以把互斥锁看做某种意义上的全局变量。在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进程操作。若其他线程希望上锁一个已经被上锁的互斥锁,则该线程会被挂起,直到上锁的线程释放掉互斥锁为止。
互斥锁机制不要包括以下基本函数:
- 互斥锁初始化:pthread_mutex_init()
- 互斥锁上锁:pthread_mutex_lock()
- 互斥锁判断上锁:pthread_mutex_trylock()
- 互斥锁解锁:pthread_mutex_unlock()
- 消除互斥锁:pthread_mutex_destory()
互斥锁可以分为快速互斥锁、递归互斥锁和检错互斥锁。一般默认属性为快速互斥锁
- 快速互斥锁:指调用线程会阻塞直至拥有互斥锁的线程解锁为止
- 递归互斥锁:能够成功地返回,并且增加调用线程在互斥上加锁的次数
- 检错互斥锁:快速互斥锁的非阻塞版本,它会立即返回并返回一个错误信息
1.2 互斥锁函数说明
pthread_mutex_init()
/*****pthread_mutex_init()*****/
函数原型:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
传 入 值:mutex 互斥锁
mutexattr -->PTHREAD_MUTEX_INITIALIZER 创建快速互斥锁
-->PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 创建递归互斥锁
-->PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP 创建检错互斥锁
返 回 值:成功返回0;失败返回错误码
pthread_mutex_lock()
pthread_mutex_trylock()
pthread_mutex_unlock()
pthread_mutex_destory()
/*****pthread_mutex_****()*****/
函数原型:int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_destory(pthread_mutex_t *mutex)
传 入 值:mutex 互斥锁
返 回 值:成功返回0;失败返回-1
2. 信号量线程控制
2.1 信号量编程说明
信号量也就是操作系统中所用到的PV原子操作,它广泛应用于进程或线程间的同步与互斥。信号量本质上是一个非负的正数计数器,它被用来控制对公共资源的访问。PV原子操作的原理如下:
PV原子操作是对整数计数器信号量sem的操作。一次P操作使sem减1,而一次V操作使sem加1。进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。当信号量sem的值>=0时,该进程(或线程)具有公共资源的访问权限;相反,当信号量sem的值<0时,该进程(或线程)就将阻塞直到信号量sem的值>=0为止。
PV原子操作主要用于进程或线程间的同步和互斥这两种典型情况。若用于互斥,几个进程(或线程)往往只设置一个信号量sem;若用于同步,往往会设置多个信号量,并安排不同的初始值来实现它们之间的顺序执行。
2.2 信号量函数说明
线程间同步和互斥操作常见函数:
- 创建信号量并初始化:sem_init()
- P操作,在信号量>0时,将信号量的值减1:sem_wait()和sem_trywait(),当信号量<0时,前者会阻塞进程,而后者会立即返回
- V操作,将信号量的值加1,同时发出信号唤醒等待的进程:sem_post()
- 得到信号量的值:sem_getvalule()
- 删除信号量:sem_destory()
/*****sem_init()*****/
函数原型:int sem_init(sem_t *sem, int pshared, unsigned int value)
传 入 值:sem 信号量指针
pshared 决定信号量能否在几个进程间共享,一般取0表示是当前进程的局部信号量
value 信号量初始化值
返 回 值:成功返回0;失败返回-1
sem_wait()
sem_trywait()
sem_post()
sem_getvalule()
sem_destory()
/*****sem_****()*****/
函数原型:int sem_wait(sem_t *sem)
int sem_trywait(sem_t *sem)
int sem_post(sem_t *sem)
int sem_getvalule(sem_t *sem)
int sem_destory(sem_t *sem)
传 入 值:sem 信号量指针
返 回 值:成功返回0;失败返回-1
3. 函数实例
在thread.c的代码基础上增加互斥锁功能,实现原本独立与无序的多个线程按顺序执行
/*****thread_mutex.c*****/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define THREAD_NUMBER 3 //线程数
#define REPEAT_NUMBER 5 //每个线程中的小任务数
#define DELAY_TIME_LEVELS 10.0 //小任务之间的最大时间间隔
pthread_mutex_t mutex;
void *thrd_func(void *arg){
int thrd_num = (int)arg;
int delay_time = 0,count = 0;
int res;
res = pthread_mutex_lock(&mutex); //互斥锁上锁
if(res){
printf("Thread %d lock failed\n",thrd_num);
pthread_exit(NULL);
}
printf("Thread %d is starting\n",thrd_num);
for(count = 0;count < REPEAT_NUMBER;count++){
delay_time = (int)(rand()*DELAY_TIME_LEVELS/(RAND_MAX)) + 1;
sleep(delay_time);
printf("\tThread %d: job %d delay = %d\n",thrd_num,count,delay_time);
}
printf("Thread %d finished\n",thrd_num);
pthread_exit(NULL);
}
int main(){
pthread_t thread[THREAD_NUMBER];
int no = 0,res;
void * thrd_ret;
srand(time(NULL));
pthread_mutex_init(&mutex,NULL); //互斥锁初始化
for(no = 0;no < THREAD_NUMBER;no++){
res = pthread_create(&thread[no],NULL,thrd_func,(void*)no);
if(res != 0){
printf("Create thread %d failed\n",no);
exit(res);
}
}
printf("Create thread sucess\nWaiting for threads to finish...");
for(no = 0;no < THREAD_NUMBER;no++){
res = pthread_join(thread[no],&thrd_ret);
if(!res)
printf("Thread %d joined\n",no);
else
printf("Thread %d join failed\n",no);
pthread_mutex_unlock(&mutex); //互斥锁解锁
}
pthread_mutex_destory(&mutex);
return 0;
}
运行结果如下,可见3个线程之间的运行顺序与创建线程的顺序相同
linux@linux-virtual-machine:~/andy/proc$ ./thread_mutex
Create threads sucess
Waiting for threads to finish...
Thread 0 is starting
Thread 0: job 0 delay = 7
Thread 0: job 1 delay = 7
Thread 0: job 2 delay = 6
Thread 0 finished
Thread 0 joined
Thread 1 is starting
Thread 1: job 0 delay = 3
Thread 1: job 1 delay = 5
Thread 1: job 2 delay = 10
Thread 1 finished
Thread 1 joined
Thread 2 is starting
Thread 2: job 0 delay = 6
Thread 2: job 1 delay = 10
Thread 2: job 2 delay = 8
Thread 2 finished
Thread 2 joined
关注我的公众号,共同交流学习嵌入式开发相关技术: