互斥锁和条件变量

为了允许在线程或进程之间共享数据,同步是必须的,互斥锁和条件变量是同步的基本组成部分.

1.互斥锁

互斥锁是用来保护临界区资源,实际上保护的是临界区中被操纵的数据,互斥锁通常用于保护由多个线程或进程分享的共享数据,一般是一些可供线程间使用的全局变量,来达到线程同步的目的,保证任何时刻只有一个线程或进程在执行其中的代码,  

互斥锁的函数:  

1.pthread_mutex_init(pthread_mutex_t  *mutex,const pthread_mutexattr_t   *mutexattr)函数   初始化一个互斥锁 使用互斥锁之前必须进行初始化操作,初始化有两种方法,1.静态赋值法.对于静态分配的互斥锁一般用宏赋值的方式初始化eg:static  pthread_mutex_t mutex = PTHREAD_NUTEX_INITALIZER;2.对于动态分配的互斥锁(如调用malloc)或分配在共享内存中,则必须调用int    pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t  *mutexattr)初始化;  

函数中的mutexattr是锁的属性:  

属性值:PTHREAD_MUTEX_TIMED_NP    普通锁:当一个线程加锁后,其余请求的线程形成等待队列,解锁后按优先级获得锁; PTHREAD_MUTEX_RECURSIVE_NP:嵌套锁,允许一个线程队同一个锁多次加锁,并通过多次unlock 解锁,如果是不同线程请求,则在解锁时重新竞争; PTHREAD_MUTEX_ERRORCHECK_NP: 检错锁:在同一个线程请求同一个锁的情况下,返回EDEADLK,否则执行的动作和类型PTHREAD_MUTEX_TIMED_NP相同; PTHREAD_MUTEX_ADAPTIVE_NP 适应锁:解锁后重新竞争 2.int  pthread_mutex_destroy(pthread_mutex_t  *mutex)函数  注销一个互斥锁 清除一个互锁意味着释放它占用的资源,清除锁时要求当前处于开放状态,若锁处于锁定状态,函数返回EBUSY,该函数成功执行返回0. 3.int pthread_mutex_lock(pthread_mutex_t *mutex)函数    加锁,如果不成功,阻塞等待 用pthread_mutex_lock(pthread_mutex_t *mutex)函数加锁时,如果mutex 已经被锁住,当前尝试加锁的线程就会阻塞,直到互斥锁被其它线程释放. 注意:加锁时,不论那种类型的锁,都不可能被两个不同的线程同时得到,其中一个必须等待解锁,在同一进程中的线程,如果加锁后没有解锁,则其它线程将无法在获得该锁. 4. int pthread_mutex_unlock(pthread_mutex_t *mutex);                 解锁; 用此函数有两个条件,1.互斥锁必须处于加锁状态,调用本函数的线程必须是给互斥锁加锁的线程; 5.pthread_mutex_trylock(pthread_mutex_t *mutex);                   测试锁,如果不成功则立即返回,错误代码为BEBUSY;

2.条件变量:

条件变量是利用线程间共享的全局变量进行同步的一种机制,条件变量宏观上类似于if  语句,符合条件就能执行某段程序,否则只能等待条件成立. 使用条件变量主要包括两个动作:一个等待使用资源的线程"条件变量被设置为真";  另一个线程在使用完资源后"设置条件为真",这样就能保证线程间的同步了,条件变量的操作函数: int pthread_cond_init ( pthread_cond_t  *cond,pthread_condattr_t  *cond_attr)函数: 其中,cond_attr参数是条件变量的属性,由于没有得到实现,所以它的值通常为NULL; 等待函数有两个: int pthread_cond_wait([pthread_cond_t *cond,pthread_mutex_t *mutex); 该函数释放由mutex指向的互斥锁,同时使当前线程关于cond指向的条件变量阻塞.直到条件信号唤醒,通常条件表达式在互斥锁的保护下求值,如果条件表达式为假,那么线程基于条件变量阻塞,当一个线程改变条件变量的值时,条件变量获得一个信号,使等待条件变量的线程退出阻塞状态. int  pthread_cond_timedwait函数和pthread_cond_wait函数用法类似,差别在于pthread_cond_timewait函数将阻塞直到条件变量获得信号或经过由 abstime指定的时间,也就是说,如果在给定时刻的条件前没有满足,就会ETIMEOUT,结束等待. 线程被条件变量阻塞后,可通过函数int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); pthread_cond_signal()激活一个等待条件成立的线程,存在多个等待线程时,按入队顺序激活其中一个,而pthread_cond_broadcast() 则激活所有等待的线程. 当一个条件变量不在使用时,需要将其清除,清除一个条件变量通过调用 int pthread_cond_destroy(pthread_cond_t  *cond)来清除,此函数清除由cond指向的条件变量,注意:只有在没有线程的等待的该条件的时候才能清除这个条件变量.否则返回EBUSY. 来看个例子:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void my_err(const char * err_string,int line)
{
	fprintf(stderr,"line:%d",line);
	perror(err_string);
	exit(1);
}
typedef struct private_tag
{
	pthread_t thread_id;
	char *string;
}private_t;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_key_t key;
int count = 0;

void *key_get(void)
{
	void *value = NULL;
	int status;

	value = pthread_getspecific(key);
	if(value == NULL)
	{
		value=malloc(sizeof(pthread_key_t));
		if(value == NULL)
		{
			my_err("malloc",__LINE__);
		}
	status = pthread_setspecific(key,value);
	if(status!=0)
	{
		my_err("set",__LINE__);
	}
       }
	return value;
}
void *thread_routine(void *arg)
{
	private_t value = NULL;
	value=(private_t *)key_get();
	value->thread_id=pthread_self();
	value->string=(char *)arg;
	printf("\"%s\" staring.....\n",value->string);
	sleep(2);
}

结果:

yang@liu:~/syc/第八章$ ./mutex 
条件变量学习!
线程1正在运行....
线程1得到了这个条件!
线程2正在运行!
线程2得到了这个条件!
线程2正在运行!
线程2得到了这个条件!
线程2正在运行!
线程2得到了这个条件!
线程2正在运行!
线程2得到了这个条件!
线程1正在运行....
线程1得到了这个条件!

上面只是运行结果的一部分,从结果可以看出,thread1和thread2通过条件变量同步运行,在线程thread1和thread2中可以看出,条件变量要配合互锁使用,这样可以防止多个线程同时请求pthread_cond_wait. 来看两个例子:实现生产者—消费者问题,先只考虑多个生产者线程之间的同步,直到所有的生产者线程都完成工作以后,才启动消费者线程

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t mutex;
pthread_cond_t  cond;
void *thread1(void *arg)
{
	pthread_cleanup_push(pthread_mutex_unlock,&mutex);
	while(1)
	{
		printf("线程1正在运行....\n");
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond,&mutex);
		printf("线程1得到了这个条件!\n");
		pthread_mutex_unlock(&mutex);
		sleep(4);
	}
	pthread_cleanup_pop(0);
}
void *thread2(void *arg)
{
	while(1)
	{
		printf("线程2正在运行!\n");
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond,&mutex);
		printf("线程2得到了这个条件!\n");
		pthread_mutex_unlock(&mutex);
		sleep(1);
	}
}
int main(void)
{
	pthread_t thid1,thid2;
	printf("条件变量学习!\n");
	pthread_mutex_init(&mutex,NULL);
	pthread_cond_init(&cond,NULL);
	pthread_create(&thid1,NULL,(void *)thread1,NULL);
	pthread_create(&thid2,NULL,(void *)thread2,NULL);
	do{
		pthread_cond_signal(&cond);
	  }while(1);
	sleep(50);
	pthread_exit(0);
}

结果:

yang@liu:~/syc/第八章$ ./mutexcond  1000000 5
生产者正在工作!
生产者正在工作!
生产者正在工作!
生产者正在工作!
生产者正在工作!
count[0] = 206634
count[1] = 209573
count[2] = 188241
count[3] = 197221
count[4] = 198331
消费者正在工作!

可以看出,生产者进程生产完,消费者进程才开始工作! 2.通过条件变量判断判断,当消费者等待生产者,当生产者有生产时;即唤醒消费者,消费者开始工作.代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>

#define     MAXNITEMS        1000000
#define     MAXNTHREADS     100

int nitems;

struct
{
    pthread_mutex_t     mutex;
    int                 buff[MAXNITEMS];
    int                 nput;
    int                 nval;
} shared = {
    PTHREAD_MUTEX_INITIALIZER
};
//条件变量
struct {
    pthread_mutex_t mutex;  
    pthread_cond_t  cond;
    int nready;
}nready = {
  PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER
};

void *produce(void*);
void *consume(void*);

int main(int argc,char *argv[])
{
    int     i,nthreads,count[MAXNTHREADS];
    pthread_t tid_produce[MAXNTHREADS],tid_consume;
    if(argc != 3)
    {
        printf("usage: producongs2 <#itmes> <#threads>.\n");
        exit(0);
    }
    nitems = atoi(argv[1]);
    nthreads = atoi(argv[2]);
    pthread_setconcurrency(nthreads+1);
    for(i=0;i<nthreads;++i)
    {
        count[i] = 0;
        pthread_create(&tid_produce[i],NULL,produce,&count[i]);
    }
    pthread_create(&tid_consume,NULL,consume,NULL);
    for(i=0;i<nthreads;i++)
    {
        pthread_join(tid_produce[i],NULL);
        printf("count[%d] = %d\n",i,count[i]);
    }
    pthread_join(tid_consume,NULL);
    exit(0);
}

void *produce(void *arg)
{
    printf("producer begins work\n");
    for(; ;)
    {
        pthread_mutex_lock(&shared.mutex);
        if(shared.nput >= nitems)
        {
            pthread_mutex_unlock(&shared.mutex);
            return ;
        }
        shared.buff[shared.nput] = shared.nval;
        shared.nput++;
        shared.nval++;
        pthread_mutex_unlock(&shared.mutex);
        pthread_mutex_lock(&nready.mutex);
        if(nready.nready == 0)
            pthread_cond_signal(&nready.cond); //通知消费者
        nready.nready++;
        pthread_mutex_unlock(&nready.mutex);
        *((int*) arg) += 1;
    }
}
void *consume(void *arg)
{
    int     i;
    printf("consuemer begins work.\n");
    for(i=0;i<nitems;i++)
    {
        pthread_mutex_lock(&nready.mutex);
        while(nready.nready == 0)
            pthread_cond_wait(&nready.cond,&nready.mutex); //等待生产者
        nready.nready--;
        pthread_mutex_unlock(&nready.mutex);
        if(shared.buff[i] != i)
            printf("buff[%d] = %d\n",i,shared.buff[i]);
    }
    return;
}

结果:

yang@liu:~/syc/第八章$ ./mutexcond  1000000 5
producer begins work
producer begins work
consuemer begins work.
producer begins work
producer begins work
producer begins work
count[0] = 182165
count[1] = 207427
count[2] = 183748
count[3] = 236607
count[4] = 190053

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值