一、互斥锁基本原理
互斥锁以排他方式防止共享数据被并发访问。互斥锁为一个二元变量,其状态分为开锁和上锁,将某个共享资源与某个特定互斥锁在逻辑上绑定(即要申请该资源必须先获取锁),对该共享资源的访问操作如下:
1.在访问该资源前,首先申请该互斥锁,如果该互斥锁处于开锁状态,则申请到该锁对象,并占有该锁(使该锁处于锁定状态),以防止其他线程访问该资源;如果该锁处于锁定状态,默认阻塞当前线程。
2.只有锁定改互斥锁的进程才能释放该互斥锁,其他线程释放操作无效。
3.互斥锁的主要作用是保证线程执行完整性。
二、互斥锁操作流程
1.定义一个全局的锁;
2.初始化锁;
3.创建线程;
4.上锁、操作公共资源、解锁;
5.线程退出,释放资源(销毁锁)。
三、互斥锁基本操作函数
互斥锁基本操作函数如下表所示:
1.初始化互斥锁
在使用互斥锁前,需要定义互斥锁(全局变量),定义互斥锁的代码入下:
phtread_mutex_t lock;
初始化互斥锁的函数为pthread_mutex_init
,互斥锁函数声明如下:
/*Initialize a mutex*/
int pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutexattr_t* mutexattr);
第1个参数mutex是指向要初始化的互斥锁的指针。
第2个参数mutexattr是指向属性对象的指针,该属性对象定义要初始化的互斥锁的属性。若该指针为NULL时,使用默认属性。
使用默认属性初始化互斥锁代码如下:
phtread_mutex_t mp;
int ret = pthread_mutex_init(&mp, NULL);
使用自定义属性初始化互斥锁代码如下:
pthread_mutexattr_t mattr;
phtread_mutex_t mp;
int ret = pthread_mutex_init(&mp, &mattr);
pthread_mutex_init()
函数返回值为0表示函数执行成功。否则,将返回错误编号以指明错误。
此外,还可以使用宏PTHREAD_MUTEX_INITIALIZER
初始化今天分配的互斥锁。此宏定义如下:
/*come from /usr/include/pthread.h*/
#define PTHREAD_MUTEX_INITIALIZER { { 0, } }
使用宏PTHREAD_MUTEX_INITIALIZER
初始化互斥锁代码如下:
phtread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER;
2.申请互斥锁
如果一线程要占用一共享资源,必须先申请对应的互斥锁。pthread_mutex_lock()
函数以阻塞的方式申请互斥锁,其函数申明如下:
int pthread_mutex_lock(pthread_mutex_t* mutex);
pthread_mutex_trylock()
函数以非阻塞方式申请互斥锁,函数申明如下:
int pthread_mutex_trylock(pthread_mutex_t* mutex);
pthread_mutex_lock()
和pthread_mutex_trylock()
执行成功时返回0。否则,返回一个错误编号,以指明错误。
3.释放互斥锁
pthread_mutex_unlock()
函数用来释放互斥锁,其函数申明如下:
/*Unlock a mutex*/
int pthread_mutex_unlock(pthread_mutex_t* mutex);
参数mutex为执行要解锁的互斥锁的指针。释放操作只能由占有该互斥锁的线程完成。该函数执行成功时返回0。否则,返回指明错误的错误编号(未设置errno变量)。
4.销毁互斥锁
pthread_mutex_destroy()
函数用来释放互斥锁,其函数申明如下:
/*Destroy a mutex*/
int pthread_mutex_destroy(pthread_mutex_t* mutex);
参数mutex为执行要解锁的互斥锁的指针。该函数执行成功时返回0。否则,返回指明错误的错误编号。
四、示例
下面通过3个线程来计算1+2+3+…+100的值,代码如下:
#include <stdio.h>
#include <pthread.h>
int sum = 0;
void* pth_add1(void* arg)
{
int i;
for (i=1; i<20; i++) {
sum += i;
usleep(10000);
}
pthread_exit(0);
}
void* pth_add2(void* arg)
{
int i;
for (i=20; i<60; i++) {
usleep(10000);
sum += i;
}
pthread_exit(0);
}
void* pth_add3(void* arg)
{
int i;
for (i=60; i<=100; i++) {
sum += i;
usleep(10000);
}
pthread_exit(0);
}
int main()
{
pthread_t tid1, tid2, tid3;
int ret;
ret = pthread_create(&tid1, NULL, pth_add1, NULL);
if (0 != ret) {
perror("create pthead error");
return -1;
}
ret = pthread_create(&tid2, NULL, pth_add2, NULL);
if (0 != ret) {
perror("create pthead error");
return -1;
}
ret = pthread_create(&tid3, NULL, pth_add3, NULL);
if (0 != ret) {
perror("create pthead error");
return -1;
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
printf("sum = %d\n", sum);
return 0;
}
运行结果如下所示:
由上图可知,四次运行结果中只有一次结果是对的,为什么会出现这样的情况呢?由于3个线程访问共享资源sum,而没有使用互斥锁导致,下面给每个线程加互斥锁:
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t lock;
int sum = 0;
void* pth_add1(void* arg)
{
int i;
// 申请互斥锁
pthread_mutex_lock(&lock);
for (i=1; i<20; i++) {
sum += i;
usleep(10000);
}
// 释放互斥锁
pthread_mutex_unlock(&lock);
pthread_exit(0);
}
void* pth_add2(void* arg)
{
int i;
// 申请互斥锁
pthread_mutex_lock(&lock);
for (i=20; i<60; i++) {
usleep(10000);
sum += i;
}
// 释放互斥锁
pthread_mutex_unlock(&lock);
pthread_exit(0);
}
void* pth_add3(void* arg)
{
int i;
// 申请互斥锁
pthread_mutex_lock(&lock);
for (i=60; i<=100; i++) {
sum += i;
usleep(10000);
}
// 释放互斥锁
pthread_mutex_unlock(&lock);
pthread_exit(0);
}
int main()
{
pthread_t tid1, tid2, tid3;
int ret;
// 初始化锁
pthread_mutex_init(&lock, NULL);
ret = pthread_create(&tid1, NULL, pth_add1, NULL);
if (0 != ret) {
perror("create pthead error");
return -1;
}
ret = pthread_create(&tid2, NULL, pth_add2, NULL);
if (0 != ret) {
perror("create pthead error");
return -1;
}
ret = pthread_create(&tid3, NULL, pth_add3, NULL);
if (0 != ret) {
perror("create pthead error");
return -1;
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
// 销毁锁
pthread_mutex_destroy(&lock);
printf("sum = %d\n", sum);
return 0;
}
运行结果如下:
由上图可知,4次运行结果均正确,这便证明互斥锁可以确保线程执行的完整性,故对共享资源的访问一定要用互斥锁保护起来。