Linux线程同步机制一--互斥锁

时间:2022-12-12 18:07:17

一、互斥锁基本原理

  互斥锁以排他方式防止共享数据被并发访问。互斥锁为一个二元变量,其状态分为开锁上锁,将某个共享资源与某个特定互斥锁在逻辑上绑定(即要申请该资源必须先获取锁),对该共享资源的访问操作如下:
  1.在访问该资源前,首先申请该互斥锁,如果该互斥锁处于开锁状态,则申请到该锁对象,并占有该锁(使该锁处于锁定状态),以防止其他线程访问该资源;如果该锁处于锁定状态,默认阻塞当前线程。
  2.只有锁定改互斥锁的进程才能释放该互斥锁,其他线程释放操作无效。
  3.互斥锁的主要作用是保证线程执行完整性

二、互斥锁操作流程

  1.定义一个全局的锁;
  2.初始化锁;
  3.创建线程;
  4.上锁、操作公共资源、解锁;
  5.线程退出,释放资源(销毁锁)。

三、互斥锁基本操作函数

  互斥锁基本操作函数如下表所示:
 Linux线程同步机制一--互斥锁

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;
}

运行结果如下所示:
Linux线程同步机制一--互斥锁

  由上图可知,四次运行结果中只有一次结果是对的,为什么会出现这样的情况呢?由于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;
}

运行结果如下:
Linux线程同步机制一--互斥锁
  由上图可知,4次运行结果均正确,这便证明互斥锁可以确保线程执行的完整性,故对共享资源的访问一定要用互斥锁保护起来