如果在某一时刻值只允许一个线程来访问某部分资源,保证共享数据操作的完整性,那么这种操作就成为互斥。互斥锁的作用就是对这种临界区进行保护,内部是对一个变量的原子操作(置0或置1)来决定能否获得锁来访问。
1、初始化
可以直接在定义时进行静态初始化:
pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER;
或者调用pthread_mutex_init函数动态初始化为NULL:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
2、加锁与解锁API函数
int pthread_mutex_lock(pthread_mutex_t *mutex); // 一直等待到可用
int pthread_mutex_trylock(pthread_mutex_t *mutex); // 立即返回(EBUSY)或加锁
int pthread_mutex_unlock(pthread_mutex_t *mutex); // 释放互斥量
3、示例
先看不加锁的线程操作:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int i = 0;
void *pthreadFunc(void *pvoid)
{
while(i < 200)
{
printf("pthreadFunc %s: %d\n", (char *)pvoid, i++); // 改变变量的值
usleep(500);
}
pthread_exit(NULL);
}
int main()
{
pthread_t tid[2];
pthread_create(&tid[0], NULL, pthreadFunc, "0");
pthread_create(&tid[1], NULL, pthreadFunc, "1");
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
return 0;
}
编译运行一下查看结果(这里抽取异常的一部分):
pthreadFunc 0: 0
pthreadFunc 1: 1
pthreadFunc 0: 2
pthreadFunc 1: 3
pthreadFunc 0: 5
pthreadFunc 1: 4
pthreadFunc 0: 6
pthreadFunc 1: 7
pthreadFunc 1: 8
pthreadFunc 0: 9
pthreadFunc 0: 10
pthreadFunc 1: 11
pthreadFunc 1: 12
pthreadFunc 0: 13
pthreadFunc 0: 14
pthreadFunc 1: 14
pthreadFunc 1: 15
pthreadFunc 0: 16
pthreadFunc 1: 17
pthreadFunc 0: 18
pthreadFunc 1: 19
pthreadFunc 0: 20
pthreadFunc 0: 21
pthreadFunc 1: 21
从输出结果可以看到,在4、5、14、21几个位置出现了顺序或重复的错误,这是因为两个线程同时对共享数据 i 进行操作导致的。所以针对这种情况,可以引入互斥锁改进程序:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
int i = 0;
pthread_mutex_t g_Mutex = PTHREAD_MUTEX_INITIALIZER;
void *pthreadFunc(void *pvoid)
{
while(i < 200)
{
pthread_mutex_lock(&g_Mutex); // 加锁之后其他线程需要等到解锁才能对同一把锁上锁,否则阻塞等待
printf("pthreadFunc %s: %d\n", (char *)pvoid, i++); // 改变变量的值
usleep(500);
pthread_mutex_unlock(&g_Mutex); // 解锁,其他线程可以对这个锁加锁了
}
pthread_exit(NULL);
}
int main()
{
pthread_t tid[2];
pthread_create(&tid[0], NULL, pthreadFunc, "0");
pthread_create(&tid[1], NULL, pthreadFunc, "1");
pthread_join(tid[0], NULL);
pthread_join(tid[1], NULL);
pthread_mutex_destroy(&g_Mutex);
return 0;
}
再次编译运行可以看到程序不会出现顺序或者重复的情况了,因为 i 的值在某个时刻只有一个线程在操作。