一道题分析和实现 多线程之互斥量和条件变量的配合使用

题目:使用多线程,要求按序打印出4次abcd
1.只使用互斥量mutex
忙等,直到能够获得进入临界区的条件

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#define THRNUM 4

//print abcd to stdout
//4个互斥量之间协作
static pthread_mutex_t mut[THRNUM];

static int next(int n){
	if(n + 1 == THRNUM){
		return 0;
	}
	return n + 1;
}

static void* thr_fun(void* p){
	int n = (int)p;// n = 0 1 2 3
	int c = 'a' + n;
	//lock a,unlock b
	int m = 4;
	while(m--){
		pthread_mutex_lock(mut + n);
		write(1, &c, 1);
		
		//解锁下一个字符的互斥量
		pthread_mutex_unlock(mut + next(n));
	}
	pthread_exit(NULL);
}

int main(){
	pthread_t tid[THRNUM];
	int err,i;

	for(i = 0; i < THRNUM; ++i){
		pthread_mutex_init(mut + i, NULL);
		pthread_mutex_lock(mut + i);
		err = pthread_create(tid + i, NULL, thr_fun, (void*)i);
		if(err){
			fprintf(stderr, "pthread_create():%s\n", strerror(err));
			exit(1);
		}
	}
	//最后别忘记释放自己的锁
	pthread_mutex_unlock(mut + 0);
	alarm(5);
	
	//回收资源
	for(i = 0; i < THRNUM; ++i){
		pthread_join(tid[i], NULL);
	}
	exit(0);
}

2.使用互斥量mutex和条件变量cond
互斥量和条件变量配合变成通知法

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#define THRNUM 4

//print abcd to stdout

static int num = 0;

//静态初始化
static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;


//使用cond变成通知法

static int next(int n){
	if(n + 1 == THRNUM){
		return 0;
	}
	return n + 1;
}

static void* thr_fun(void* p){
	//n == thread arg
	int n = (int)p;//0 1 2 3
	int c = 'a' + n;
	//lock a,unlock b

	int m = 4;
	//char buf[4];
	while(m--){
		//pthread_mutex_lock(mut + n);
		pthread_mutex_lock(&mut);


		//临界区内等待,发通知
		//锁住的是自己的锁
		while(num != n){
			//等待 mum = n
			//抢到的锁,但是不是自己的锁则等待
			pthread_cond_wait(&cond, &mut);
		}

		
		write(1, &c, 1);	
		
		//puts(" ");
		//fprintf(stdout, &c, NULL);
		
		//取下一个
		num = next(num);
		//a在打印, bcd在等待
		//唤醒的也不知道是哪个线程,避免不了产生惊群
		pthread_cond_broadcast(&cond);//发通知
		pthread_mutex_unlock(&mut);
	}
	pthread_exit(NULL);
}

int main(int argc, char* argv[]){
	pthread_t tid[THRNUM];
	int err,i;

	for(i = 0; i < THRNUM; ++i){
		//pthread_mutex_init(mut + i, NULL);
		//pthread_mutex_lock(mut + i);

		err = pthread_create(tid + i, NULL, thr_fun, (void*)i);
		if(err){
			fprintf(stderr, "pthread_create():%s\n", strerror(err));
			exit(1);
		}
	}
	//pthread_mutex_unlock(mut + 0);
	alarm(5);// 非正常结束
	//正常结束:添加一个处理函数,注册行为,join and destroy


	for(i = 0; i < THRNUM; ++i){
		pthread_join(tid[i], NULL);
	}

	pthread_mutex_destroy(&mut);
	pthread_cond_destroy(&cond);

	exit(0);
}

以上编译得到的结果为:

abcdabcdabcdabcd

总结:

(1)第一种方法注意点,最后别忘记释放a的锁

(2)第二种方法,使用条件变量变成了通知法,避免了忙等的情况

存在问题:可能线程惊群
产生条件:当一个线程解锁并通知其他线程的时候,就会出现惊群的现象
正常的用法:
所有线程共用一个锁,共用一个条件变量
当pthread_cond_signal通知时,就可能会出现惊群

解决惊群的方法:
所有线程共用一个锁,每个线程有自已的条件变量
pthread_cond_signal通知时,定向通知某个线程的条件变量,不会出现惊群

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页