Linux多线程编程|线程同步与互斥

线程之间的同步与互斥

由于线程共享进程的资源和地址空间,因此在对这些资源进行操作时,必须考虑到线程间资源访问的同步和互斥问题。以下主要介绍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

猜你喜欢

转载自blog.csdn.net/Chuangke_Andy/article/details/108356000