前言
上篇文章【Linux多线程编程】3. 多线程共享资源介绍了线程共享资源与独享资源,要想正确的访问线程共享资源,必须进行线程同步,线程同步有四种方式——互斥锁、读写锁、条件变量、信号量,本文先介绍第一种,互斥锁。
互斥锁
顾名思义,互斥锁是一把相互排斥的锁,也就是A线程拿到了锁,B线程必须等待才能拿到这把锁。好比很多人去上厕所,厕所只有一个门,每次允许一个人上厕所,率先拿到锁的人可以进去然后锁住厕所,后面想来的人只能在门口等待上一个人出来解锁,然后才能加锁上厕所。
也就是,多线程一起去竞争一把锁,线程A拿到该锁,其他线程只能阻塞等待解锁。
对于锁的使用有如下步骤
- 创建并初始化一把互斥锁
- 线程A加锁
- 线程A解锁
- 回收锁资源(若该锁不再被使用)
接下来将逐一介绍。
创建互斥锁
pthread_mutex_t mutex;
互斥锁用 pthread_mutex_t 表示,此句创建了一个名为 mutex 的锁,这把锁要干什么?锁什么资源,目前,还没有定义。
初始化互斥锁
int pthread_mutex_init(pthread_mutex_t* restrict mutex, const pthread_mutexattr_t* restrict attr);
参数解释
restrict mutex:互斥锁的地址,传 &mutex
restrict attr:3属性,默认为NULL
加解锁
int pthread_mutex_lock(pthread_mutex_t* mutex); // 加锁
int pthread_mutex_unlock(pthread_mutex_t* mutex); // 解锁
若某个线程已经执行pthread_mutex_lock
进行了加锁,其他线程再次执行pthread_mutex_lock
企图对同一把锁进行加锁会被阻塞,直到持有该锁的线程调用pthread_mutex_unlock
释放该锁。
位于pthread_mutex_lock
和pthread_mutex_unlock
之间的代码被称为临界区代码,位于临界区代码内的对于线程共享资源的访问都是安全的。
回收锁资源
int pthread_mutex_destroy(pthread_mutex_t *mutex);
回收使用pthread_mutex_init
初始化的锁。
举例
现在有了互斥锁,我们可以对上篇文章的生产者消费者的代码进行改进。
首先需要分析:哪些资源是临界资源(线程共享资源)?这些资源就是需要用锁保护的资源。
显然,全局变量total是临界资源,也就是线程要想访问total,就必须将total放置在锁的保护区内,即
{
// ...
pthread_mutex_lock(&mutex); // 加锁
total += 5; // 访问共享资源 total
printf("%s produce 5 tools, now total %d\n", __func__, total); // 访问共享资源 total
pthread_mutex_unlock(&mutex); // 解锁
// ...
}
这样修改后,我们的代码即可修改为如下的样子。
/*
* test_pthread_worker.c
*/
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <signal.h> /* for sigaction */
#include <errno.h>
#include <unistd.h>
int total = 0;
pthread_mutex_t mutex; // 定义互斥锁
static void *producer_thread(void *UnusedArg) {
while (1) {
sleep(1);
pthread_mutex_lock(&mutex); // 加锁
total += 5;
printf("%s produce 5 tools, now total %d\n", __func__, total);
pthread_mutex_unlock(&mutex); // 解锁
}
return NULL;
}
static void *consumer_thread(void *UnusedArg) {
while (1) {
sleep(1);
pthread_mutex_lock(&mutex); // 加锁
total -= 5;
printf("%s consume 5 tools, now total %d\n", __func__, total);
pthread_mutex_unlock(&mutex); // 解锁
}
return NULL;
}
int main()
{
int rc;
pthread_t producer;
pthread_t consumer;
rc = pthread_mutex_init(&mutex, NULL); // 创建线程前初始化锁
if (rc != 0) {
printf("pthread_mutex_init failed\n");
return -1;
}
rc = pthread_create(&producer, NULL, producer_thread, NULL);
if (rc != 0) {
printf("Could not create producer_thread\n");
return -1;
}
rc = pthread_create(&consumer, NULL, consumer_thread, NULL);
if (rc != 0) {
printf("Could not create consumer_thread\n");
return -1;
}
pthread_join(producer, NULL);
pthread_join(consumer, NULL);
pthread_mutex_destroy(&mutex); // 线程退出后释放锁
return 0;
}
运行这段代码,便可以安全的访问total变量。试一下吧。