APUE之线程(四)

线程同步:

        线程同步问题,可以说是每一本经典的操作系统教程都能会提到。简单的说就是对于一个变量,如果一个进程可以修改,其他的进程也可以读取或者修改这个变量的时候,就需要对这些进程进行同步,以确保他们在访问变量存储的内容的时候不会访问到无效的数据。

互斥量:

        互斥变量的数据类型用pthread_mutex_t数据类型来表示,使用前必须要初始化,可以把他设置位常量,PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以通过调用pthread_mutex_init()函数来进行初始化。如果动态分配互斥量(例如通过malloc()),那么在释放内存前需要调用pthread_mutex_destory。

#include 
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destory(pthread_mutex_t *mutex);
        要用默认的属性进行初始化互斥量,只需要把attr设置为NULL。

        对互斥量进行加锁,需要调用pthread_mutex_lock,如果互斥量已经上锁,调用线程将阻塞知道互斥量被解锁。解锁互斥量用pthread_mutex_unlock。

#Include 
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
        如果线程不希望被阻塞,那么他可以使用pthread_mutex_trylock()尝试对互斥量进行加锁。如果调用pthread_mutex_trylock()时互斥量处于未锁住的状态,那么这个函数将锁住互斥量,不会出现阻塞并返回0,否则pthread_mutex_trylock()就会失败,不能锁住互斥量,返回EBUSY。

下面的代码用于保护某个数据结构的互斥量。

/*
 *Name : list11_5.c
 *Author : Lniper
 *Date : 2016-02-17
 *Aim : Using the mutex to protect the data struct.
 */
#include 
#include 

struct foo{
	int f_count;
	pthread_mutex_t f_lock;
};

struct foo *foo_alloc(void)
{
	/* allocate the object */
	struct foo *fp;
	
	if((fp = malloc(sizeof(struct foo))) != NULL){
		fp->f_count = 1;
		if(pthread_mutex_init(&fp->f_lock, NULL) != 0){
			free(fp);
			return(NULL);
		}
		/* ...continue initialization ... */
	}
	return (fp);
}

void foo_hold(strcut foo *fp)
{
	/* add a reference to the object */
	pthread_mutex_lock(&fp->f_lock);
	fp->f_count++;
	pthread_mutex_unlock(&fp->f_lock);
}

void foo_rele(struct foo *fp)
{
	/* release a reference to the object */
	pthread_mutex_lock(&fp->f_lock);
	if(--fp->f_count == 0){/* last reference */
		pthread_mutex_unlock(&fp->f_lock);
		pthread_mutex_destory(&fp->f_lock);
		free(fp);
	}
	else{
		pthread_mutex_unlock(&fp->f_lock);
	}
}
        以上可以看出在foo_alloc函数将引用计数初始化为1是没有必要加锁,因为在这个操作前分配线程是唯一引用该对象的线程。但是在这之后如果要将该对象放到一个列表中,那么他就有可能被别的线程发现,因此需要首先对他加锁。

总结一下,线程在使用对象前需要对对象的引用计数加1,当对象使用完毕,需要对引用计数减1。当最后一个引用被释放的时候,对象所占用的内存空间就被释放了。

避免死锁:

        如果线程对同一个互斥量加锁两次,那么自身就会陷入死锁状态。比如说,程序中使用多个互斥量时,如果允许一个线程一直占用第一个互斥量,并且在试图锁住第二个互斥量的时候处于阻塞的状态,但是拥有第二个互斥量的线程试图锁住第一个互斥量的时候,这个时候如果没有“外力”的介入打破这种僵局就会产生死锁。

        下面这个例子用以描述互斥量的使用方法,当同时需要两个互斥量的时候,总是让他们以相同的顺序进行加锁,从而来避免死锁的发生。第二个互斥量维护者一个用于跟踪foo数据结构的散列表。这样hashlock互斥量保护的foo结构中的fh散列表和f_next散列链字段。foo结构中的f_lock互斥量保护对foo结构中的其他字段的访问。

/*
 *Name : list11_6.c
 *Author : Lniper
 *Date : 2016-02-17
 *Aim : Using double mutexs.
 */
#include 
#include 

#define NHASH 29
#define HASH(fp) (((unsigned long)fp)%NHASH)

struct foo *fh[NHASH];

pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;

struct foo{
	int f_count;
	pthread_mutex_t f_lock;
	struct foo *f_next;			/* protected by hashlock */
	int f_id;
	/* ... more stuff here ... */
};

struct foo *foo_alloc(void)
{
	/* allocate the object */
	struct foo *fp;
	int idx;
	
	if((fp = malloc(sizeof(struct foo))) != NULL){
		fp->f_count = 1;
		if(pthread_mutex_init(&fp->f_lock, NULL) != 0){
			free(fp);
			return(NULL);
		}
		idx= HASH(fp);
		pthread_mutex_lock(&hashlock);
		fp->f_next = fh[idx];
		fh[idx] = fp;
		pthread_mutex_lock(&fp->f_lock);
		pthread_mutex_unlock(&hashlock);
		/* ...continue initialization ... */
		pthread_mutex_unlock(&fp->f_lock);
	}
	return (fp);
}

void foo_hold(strcut foo *fp)
{
	/* add a reference to the object */
	pthread_mutex_lock(&fp->f_lock);
	fp->f_count++;
	pthread_mutex_unlock(&fp->f_lock);
}

struct foo *foo_find(int id)
{
	/* find an existing object */
	struct foo *fp;
	int idx;
	
	idx = HASH(fp);
	pthread_mutex_lock(&hashlock);
	for(fp = fh[idx]; fp != NULL; fp = fp->f_next){
		if(fp->f_id == id){
			foo_hold(fp);
			break;
		}
	}
	pthread_mutex_unlock(&hashlock);
	return (fp);
}

void foo_rele(struct foo *fp)
{
	/* release a reference to the object */
	struct foo *tfp;
	int idx;
	
	pthread_mutex_lock(&fp->f_lock);
	if(fp->f_count == 1){ /* last reference */
		pthread_mutex_unlock(&fp->f_lock);
		pthread_mutex_lock(&hashlock);
		pthread_mutex_lock(&fp->f_lock);
		/* need to recheck the condition */
		if(fp->f_count != 1){
			fp->f_count--;
			pthread_mutex_unlock(&fp->f_lock);
			pthread_mutex_unlock(&hashlock);
			return ;
		}
		/* remove from list */
		idx = HASH(fp);
		tfp = fh[idx];
		if(tfp == fp){
			fh[idx] = fp->f_next;
		}
		else{
			while(tfp->f_next != fp)
				tfp = tfp->f_next;
			tfp->f_next = fp->f_next;
		}
		pthread_mutex_unlock(&hashlock);
		pthread_mutex_unlock(&fp->f_lock);
		pthread_mutex_destroy(&fp->f_lock);
		free(fp);
	}
	else{
		fp->f_count--;
		pthread_mutex_unlock(&fp->f_lock);
	}
}
       这里维护两个互斥量,一个是全局互斥量hashlock,一个是结构体内部的互斥量f_lock,全局互斥量是hash表自身的互斥量,每次先加锁全局,在加锁局部的互斥量从而来避免死锁的发生。




你可能感兴趣的:(环境编程_My,endeavor)