在多线程编程中,互斥量(mutex)是用于保护共享资源的同步机制,确保在任一时刻只有一个线程能够访问共享资源。互斥量用于防止竞态条件(race conditions),确保数据一致性。
基本概念
互斥量(mutex)是一种锁机制,用于实现线程之间的互斥访问。它有两个基本操作:
- 锁定(lock):一个线程锁定互斥量以获得对共享资源的独占访问权。如果互斥量已经被其他线程锁定,调用该操作的线程会被阻塞,直到互斥量被解锁。
- 解锁(unlock):一个线程解锁互斥量,释放对共享资源的独占访问权,从而允许其他被阻塞的线程获得该资源的访问权。
在Linux中使用互斥量
在Linux中,可以使用POSIX线程库(pthread
)中的互斥量。下面是一些主要函数:
- 初始化互斥量:
pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
- 销毁互斥量:
pthread_mutex_destroy(pthread_mutex_t *mutex);
- 锁定互斥量:
pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_trylock(pthread_mutex_t *mutex);
pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout);
- 解锁互斥量:
pthread_mutex_unlock(pthread_mutex_t *mutex);
示例代码
以下是一个使用互斥量保护共享资源的简单示例:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 互斥锁的声明
pthread_mutex_t mutex;
// 共享变量
int i = 0;
// 线程函数1
void *func1(void *arg)
{
static char *p = "t1 is run";
pthread_mutex_lock(&mutex); // 获取互斥锁
for(i = 0; i < 5; i++) {
printf("func1: %d\n", i);
sleep(1);
}
printf("func1: i = %d\n", i);
pthread_mutex_unlock(&mutex); // 释放互斥锁
pthread_exit((void *)p); // 退出线程并返回信息
}
void *func2(void *arg)
{
static char *p = "t2 is run";
while(1){
if(i == 5){
pthread_mutex_lock(&mutex); // 获取互斥锁
printf("fun2: I got mutex\n");
printf("fun2=========: %d\n", i);
pthread_mutex_unlock(&mutex); // 释放互斥锁
pthread_exit((void *)p); // 退出线程并返回信息
}
}
}
int main()
{
int param = 100;
pthread_t t1;
pthread_t t2;
// 初始化互斥锁
pthread_mutex_init(&mutex, NULL);
char *pret = NULL;
char *pret2 = NULL;
// 创建线程1
int ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if(ret == 0){
printf("successfully creat a pthread1!\n");
}
else{
printf("faild to creat a pthread\n");
}
// 创建线程2
int ret2 = pthread_create(&t2, NULL, func2, (void *)¶m);
if(ret2 == 0){
printf("successfully creat a pthread2!\n");
}
else{
printf("faild to creat a pthread\n");
}
printf("I am main: %ld\n", (unsigned long)pthread_self());
// 等待线程1和2结束
pthread_join(t1, (void **)&pret);
pthread_join(t2, (void **)&pret2);
printf("main: t1 quit: %s\n", pret);
printf("main: t2 quit: %s\n", pret2);
// 销毁互斥锁
pthread_mutex_destroy(&mutex);
return 0;
}
运行结果:
解释
- 定义互斥量:使用
pthread_mutex_t
类型定义一个互斥量变量mutex
。 - 初始化互斥量:在使用互斥量之前,需要调用
pthread_mutex_init
函数初始化互斥量。 - 锁定和解锁互斥量:在访问共享资源时,使用
pthread_mutex_lock
和pthread_mutex_unlock
函数锁定和解锁互斥量,确保每次只有一个线程能够访问共享资源。 - 销毁互斥量:在不再需要互斥量时,调用
pthread_mutex_destroy
函数销毁互斥量。
注意事项
- 避免死锁:确保每个锁定的互斥量最终会被解锁,避免死锁情况的发生。
- 正确初始化和销毁:在使用互斥量前一定要初始化,使用后要销毁。
- 最小锁定范围:尽量减少锁定的范围,只在必要的代码段中使用锁,以提高并发性。
通过正确使用互斥量,可以确保多线程程序中的共享资源被安全地访问和修改,防止竞态条件的发生。
问题1:什么情况会造成死锁?
在Linux系统中,互斥锁(mutex)用于确保多个线程不会同时访问共享资源。然而,不正确的使用互斥锁可能会导致死锁。最常见的情况如下:
相互持有资源且等待对方释放
这是最经典的死锁场景,通常被称为“循环等待”条件。假设有两个线程,分别持有锁A和锁B:
- 线程1持有锁A,等待锁B。
- 线程2持有锁B,等待锁A。
这种情况下,两个线程会无限期地等待对方释放锁,导致死锁。
条件变量
条件变量(condition variable)是用于线程间协调的一种同步机制。它允许线程在特定条件不满足时等待,并在条件满足时收到通知继续执行。条件变量通常与互斥锁(mutex)结合使用,以确保对共享资源的访问是安全的。
条件变量的主要操作
-
初始化和销毁
pthread_cond_init
: 初始化条件变量。pthread_cond_destroy
: 销毁条件变量。
-
等待和唤醒
pthread_cond_wait
: 释放互斥锁并等待条件变量,直到收到信号。pthread_cond_signal
: 唤醒一个等待该条件变量的线程。pthread_cond_broadcast
: 唤醒所有等待该条件变量的线程。
使用条件变量的步骤
- 初始化条件变量和互斥锁
- 在条件不满足时等待
- 在条件满足时唤醒等待线程
- 销毁条件变量和互斥锁
相关函数介绍
1. pthread_cond_init
功能
初始化条件变量。
原型
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
参数
cond
: 指向需要初始化的条件变量的指针。attr
: 条件变量属性,如果为NULL,使用默认属性。
返回值
- 成功返回0。
- 失败返回错误码。
示例
pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
2. pthread_cond_destroy
功能
销毁条件变量。
原型
int pthread_cond_destroy(pthread_cond_t *cond);
参数
cond
: 指向需要销毁的条件变量的指针。
返回值
- 成功返回0。
- 失败返回错误码。
示例
pthread_cond_destroy(&cond);
3. pthread_cond_wait
功能
等待条件变量。在等待期间,会释放与条件变量关联的互斥锁,并在收到信号后重新获得锁。
原型
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
参数
cond
: 指向条件变量的指针。mutex
: 指向与条件变量关联的互斥锁的指针。
返回值
- 成功返回0。
- 失败返回错误码。
示例
pthread_mutex_lock(&mutex);
while (condition_is_not_met) {
pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
4. pthread_cond_signal
功能
发送信号给一个等待该条件变量的线程,唤醒它。
原型
int pthread_cond_signal(pthread_cond_t *cond);
参数
cond
: 指向条件变量的指针。
示例
pthread_mutex_lock(&mutex);
condition_is_met = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
返回值
- 成功返回0。
- 失败返回错误码。
5. pthread_cond_broadcast
功能
发送信号给所有等待该条件变量的线程,唤醒它们。
原型
int pthread_cond_broadcast(pthread_cond_t *cond);
参数
cond
: 指向条件变量的指针。
返回值
- 成功返回0。
- 失败返回错误码。
示例
pthread_mutex_lock(&mutex);
condition_is_met = 1;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
条件变量使用示例
以下是一个示例程序,展示了如何使用条件变量和互斥锁同步两个线程的操作:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 定义互斥锁和条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;
int i = 0; // 共享资源
// 线程函数1
void *func1(void *arg) {
pthread_mutex_lock(&mutex); // 加锁保护共享资源
for (i = 0; i < 5; i++) {
printf("func1: %d\n", i);
sleep(1); // 模拟工作
}
pthread_cond_signal(&cond); // 通知等待线程条件已满足
pthread_mutex_unlock(&mutex); // 释放锁
pthread_exit(NULL); // 退出线程
}
// 线程函数2
void *func2(void *arg) {
pthread_mutex_lock(&mutex); // 加锁保护共享资源
while (i < 5) { // 当条件不满足时等待
pthread_cond_wait(&cond, &mutex); // 等待条件变量的信号
}
printf("func2: %d\n", i);
pthread_mutex_unlock(&mutex); // 释放锁
pthread_exit(NULL); // 退出线程
}
int main() {
pthread_t t1, t2;
// 初始化互斥锁和条件变量
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
// 创建线程1和线程2
pthread_create(&t1, NULL, func1, NULL);
pthread_create(&t2, NULL, func2, NULL);
// 等待线程1和线程2结束
pthread_join(t1, NULL);
pthread_join(t2, NULL);
// 销毁互斥锁和条件变量
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
运行结果如下: