详解linux多线程——互斥锁、条件变量、读写锁、自旋锁、信号量

本文详细介绍了Linux多线程中的同步机制,包括互斥锁、条件变量、读写锁、自旋锁和信号量的原理及操作流程。互斥锁提供原子性和唯一性,确保资源的独占访问;条件变量用于等待特定条件成立;读写锁允许多个读取者并行访问,但限制写入者;自旋锁在内核中用于短时间资源保护;信号量用于同步和互斥控制,通过P、V操作调整计数器实现线程调度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、互斥锁(同步)

  在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。这个过程有点类似于,公司部门里,我在使用着打印机打印东西的同时(还没有打印完),别人刚好也在此刻使用打印机打印东西,如果不做任何处理的话,打印出来的东西肯定是错乱的。

  在线程里也有这么一把锁——互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。

【互斥锁的特点】:

1. 原子性:把一个互斥量锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量;

2. 唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;

3. 非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。

【互斥锁的操作流程如下】:

1. 在访问共享资源后临界区域前,对互斥锁进行加锁;

2. 在访问完成后释放互斥锁导上的锁。在访问完成后释放互斥锁导上的锁;

3. 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。

#include <pthread.h>#include <time.h>// 初始化一个互斥锁。int pthread_mutex_init(pthread_mutex_t *mutex, 
						const pthread_mutexattr_t *attr);// 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,// 直到互斥锁解锁后再上锁。int pthread_mutex_lock(pthread_mutex_t *mutex);// 调用该函数时,若互斥锁未加锁,则上锁,返回 0;// 若互斥锁已加锁,则函数直接返回失败,即 EBUSY。int pthread_mutex_trylock(pthread_mutex_t *mutex);// 当线程试图获取一个已加锁的互斥量时,pthread_mutex_timedlock 互斥量// 原语允许绑定线程阻塞时间。即非阻塞加锁互斥量。int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,const struct timespec *restrict abs_timeout);// 对指定的互斥锁解锁。int pthread_mutex_unlock(pthread_mutex_t *mutex);// 销毁指定的一个互斥锁。互斥锁在使用完毕后,// 必须要对互斥锁进行销毁,以释放资源。int pthread_mutex_destroy(pthread_mutex_t *mutex);

【Demo】(阻塞模式):

//使用互斥量解决多线程抢占资源的问题#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>#include <string.h>
 char* buf[5]; //字符指针数组  全局变量int pos; //用于指定上面数组的下标
 //1.定义互斥量pthread_mutex_t mutex; 
void *task(void *p){    //3.使用互斥量进行加锁
    pthread_mutex_lock(&mutex);
 
    buf[pos] = (char *)p;
    sleep(1);
    pos++; 
    //4.使用互斥量进行解锁
    pthread_mutex_unlock(&mutex);
} 
int main(void){    //2.初始化互斥量, 默认属性
    pthread_mutex_init(&mutex, NULL); 
    //1.启动一个线程 向数组中存储内容
    pthread_t tid, tid2;
    pthread_create(&tid, NULL, task, (void *)"zhangfei");
    pthread_create(&tid2, NULL, task, (void *)"guanyu");    //2.主线程进程等待,并且打印最终的结果
    pthread_join(tid, NULL);
    pthread_join(tid2, NULL); 
    //5.销毁互斥量
    pthread_mutex_destroy(&mutex); 
    int i = 0;    printf("字符指针数组中的内容是:");    for(i = 0; i < pos; ++i)
    {        printf("%s ", buf[i]);
    }    printf("\n");    return 0;
}

【Demo】(非阻塞模式):

#include <stdio.h>#include <pthread.h>#include <time.h>#include <string.h>
 int main (void){    int err;    struct timespec tout;
    struct tm *tmp;
    char buf[64];    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    
    pthread_mutex_lock (&lock);    printf ("mutex is locked\n");
    clock_gettime (C
<think>好的,用户想了解不同类型的机制及其应用场景,包括互斥锁条件变量自旋锁读写锁。首先我需要回忆这些的基本概念和工作原理,然后比较它们的区别和使用场景。 首先,互斥锁互斥锁用于保护共享资源,确保同一时间只有一个线程可以访问。当线程尝试获取已被定的互斥锁时,会进入阻塞状态,释放CPU资源。这在多线程环境中很常见,比如修改全局变量时。记得用户提供的引用中提到,互斥锁要么是住状态,要么是不加,一次只能一个线程加[^2]。另外,条件变量需要和互斥锁一起使用,因为条件变量在等待时会释放互斥锁,被唤醒后再重新加[^4]。 然后是条件变量条件变量通常与互斥锁配合使用,用于线程间的等待和通知机制。当某个条件不满足时,线程会等待在条件变量上,同时释放互斥锁,让其他线程有机会修改条件。当条件满足时,其他线程发送信号唤醒等待的线程。比如引用中的例子,线程在等待i等于20时,会调用pthread_cond_wait,此时会释放互斥锁,直到被唤醒后重新获得。 接下来是自旋锁自旋锁互斥锁不同,当获取失败时,线程不会阻塞,而是循环检查是否可用。这样会占用CPU资源,但避免了上下文切换的开销。适用于持有时间短的场景,比如内核中的短临界区。引用中提到自旋锁在无法确定持有时间是否短的情况下应避免使用,否则会导致CPU资源浪费。 读写锁,允许更高的并行性。读写锁有三种状态:读模式加、写模式加、不加。多个读线程可以同时持有读,而写是独占的。适用于读多写少的场景,比如配置文件的读取和偶尔的更新。引用[2]提到读写锁与互斥量类似,但允许更高的并行性,适合读操作频繁的情况[^2]。 现在需要将这些整理成结构清晰的回答,比较它们的工作原理、区别和应用场景。需要注意用户提到的引用内容,比如互斥锁的释放和条件变量的使用[^1][^4],以及读写锁互斥锁的区别[^2]。同时,用户提供的引用中有关于trylock的使用,但用户的问题没有直接提到,可能需要暂时忽略,除非在比较时有必要提及。 在区别部分,可以从阻塞行为、适用场景、性能影响等方面展开。比如互斥锁在阻塞时线程休眠,自旋锁则忙等待;读写锁区分读写,提高并发性;条件变量用于等待特定条件,必须与互斥锁配合。 应用场景方面,互斥锁适合一般的共享资源保护;读写锁适合读多写少;自旋锁适合持有时间极短且不想有上下文切换的情况;条件变量用于线程间协调,比如生产者-消费者问题。 最后生成相关问题,可能需要根据常见的问题类型,比如比较不同的优缺点,如何选择机制,或者实际应用中的例子。 需要确保回答中引用正确的文献,比如条件变量必须和互斥锁一起使用,以及读写锁的三种状态[^2]。</think>### 不同类型的机制详解 #### 1. **互斥锁(Mutex)** - **工作原理**: 通过原子操作保证共享资源的独占访问。线程尝试加时,若已被占用,则进入阻塞状态(释放CPU),直到被释放后被唤醒[^1]。 - **应用场景**: 适用于对共享资源的简单互斥操作,如全局变量修改、链表更新等。例如: ```c pthread_mutex_lock(&mutex); // 加 counter++; // 修改共享变量 pthread_mutex_unlock(&mutex); // 解 ``` #### 2. **条件变量(Condition Variable)** - **工作原理**: 与互斥锁配合使用,用于线程间的条件等待和通知。调用 `pthread_cond_wait()` 时会自动释放互斥锁并进入阻塞;当条件满足时,其他线程通过 `pthread_cond_signal()` 唤醒等待线程,后者重新获取[^3]。 - **应用场景**: 生产者-消费者模型、任务队列调度。例如: ```c pthread_mutex_lock(&mutex); while (queue_empty) pthread_cond_wait(&cond, &mutex); // 等待队列非空 // 消费数据 pthread_mutex_unlock(&mutex); ``` #### 3. **自旋锁(Spinlock)** - **工作原理**: 通过忙等待(循环检查状态)实现同步。线程在获取失败时不阻塞,持续占用CPU直到可用。 - **应用场景**: 持有时间极短的场景(如内核中断处理),避免上下文切换开销。例如: ```c spin_lock(&lock); // 快速修改共享状态 spin_unlock(&lock); ``` #### 4. **读写锁(Read-Write Lock)** - **工作原理**: 允许多个读线程共享访问,但写线程独占访问。读模式下可并行,写模式下互斥[^2]。 - **应用场景**: 读多写少的数据结构(如缓存、配置文件)。例如: ```c pthread_rwlock_rdlock(&rwlock); // 读 // 读取数据 pthread_rwlock_unlock(&rwlock); pthread_rwlock_wrlock(&rwlock); // 写 // 修改数据 pthread_rwlock_unlock(&rwlock); ``` --- ### **机制对比** | 类型 | 阻塞行为 | 适用场景 | 性能特点 | |--------------|----------------------|------------------------------|------------------------------| | **互斥锁** | 线程休眠,释放CPU | 通用共享资源保护 | 上下文切换开销 | | **条件变量** | 依赖互斥锁的休眠/唤醒 | 线程间条件协作 | 需结合互斥锁使用 | | **自旋锁** | 忙等待,不释放CPU | 短临界区、低延迟场景 | 无上下文切换,但占用CPU | | **读写锁** | 读共享,写独占 | 读多写少(如缓存、配置文件) | 提高读并发性,写操作仍串行 | --- ### **典型应用场景** 1. **互斥锁**:数据库事务的原子性保证。 2. **条件变量**:多线程任务池中的任务调度。 3. **自旋锁**:内核中中断处理程序的资源保护。 4. **读写锁**:Web服务器中频繁读取静态资源的场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值