转载出处: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
函数来试图加锁。如果函数成功返回,那么就可以是使用该互斥量。如果函数错误返回,建议线程首先解锁原来的互斥量。再过一段时间继续尝试。不过这种方法带有猜测的意思,建立在概率的基础上,程序的效率不够高。