线程同步——互斥量

时间:2023-02-05 21:38:30

转载出处:http://www.cnblogs.com/Gru--/p/5064932.html


同步的概念

    多个线程可以共享内存空间,在程序中不可避免的需要多个线程协作完成某个功能。那么这些线程就可能会使用某个公共的资源。比如说全局变量,某个文件等等。为了不产生冲突,冲突会产生在多个线程的写操作之间,而读操作则很安全。这就需要多个线程之间的同步。

互斥量的原理

    互斥量 实现同步的机制很好理解。可以将互斥量想象为锁。只有当一个线程获得该锁时才有权限对共享资源的操作。从而可以理解为该线程对共享资源上了一把锁,其他线程无权操作。在此线程操作完成之后,需要解锁以便其他线程可以获得该锁。可以想的出来,使用互斥量的代码结构应该是下面的这种形式:

/* get the lock */
get_mutex();

/* some handler code */
...

/* release the lock */
release_mutex();

值的注意的是,对于想要保护的共享资源,所有的线程都应该使用上面的代码形式,而不只是一两个线程使用互斥量。否则,还是会产生冲突。

接口函数

    互斥量变量用 pthread_mutex_t 数据类型来表示。在对其使用之前必须初始化。有静态和动态两种初始化的方式。静态方式将其设置为常量 PTHREAD_MUTEX_INITIALIZER 动态方式需要借助下面两个函数:

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

int pthread_mutex_destory(pthread_mutx_t *mutex);
两个函数返回值:若成功,返回0;否则,返回错误编号

其中 attr 参数可以设置为 NULL 表示使用默认的初始化互斥量。

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

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
两个函数返回值:若成功,返回0;否则,返回错误编

实例

互斥量的内容就这么多,写个例子来练习一下:

#include <pthread.h>
#include <stdio.h>


int global = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


void *fun1(void *arg)
{
int i = 0;
for (; i < 50; i++)
{
pthread_mutex_lock(&mutex);
global += 1;
printf("thread 1 write \t%d\n", global);
pthread_mutex_unlock(&mutex);
}
pthread_exit((void*)1);
}

void *fun2(void *arg)
{
int i = 0;
for (; i < 50; i++)
{
pthread_mutex_lock(&mutex);
global += 1;
printf("thread 2 write \t%d\n", global);
pthread_mutex_unlock(&mutex);
}
pthread_exit((void*)2);
}


int main()
{
pthread_t tid1, tid2;

if (pthread_create(&tid1, NULL, fun1, NULL) != 0)
return -1;
if (pthread_create(&tid2, NULL, fun2, NULL) != 0)
return -1;

sleep(5);
return 0;
}

死锁

    使用互斥量容易带来的一个问题是死锁。试想,现在一共有两个互斥量变量 mutex1 mutex2 pthread1 占有 mutex1 想请求 mutex2 , pthread2 占有 mutex2 想请求 mutex1那么这两个线程就会一直阻塞下去,因为没有哪个线程愿意先释放占有的互斥量。一种避免死锁的方法是,所有的线程都按照相同的顺序加锁,严格控制互斥量加锁的顺序。这种方法在互斥量很多、数据结构复杂的情况下就力不从心了。另一种方法是发送无私奉献的精神,线程首先非阻塞的 pthread_mutex_trylock 函数来试图加锁。如果函数成功返回,那么就可以是使用该互斥量。如果函数错误返回,建议线程首先解锁原来的互斥量。再过一段时间继续尝试。不过这种方法带有猜测的意思,建立在概率的基础上,程序的效率不够高。