线程互斥与线程同步
目录
线程互斥
一、概念补充
二、互斥锁(pthread_mutex_t)
线程同步
一、概念介绍
二、cond接口
三、生产者与消费者模型(“321”原则)
线程互斥
一、概念补充
1.临界资源
被保护起来的共享资源就是临界资源
2.原子性
原子性是指一个操作或一组操作在执行过程中是不可分割的,即这些操作要么全部执行成功,要么全部不执行,不存在中间状态。这种特性确保了数据的一致性和完整性,避免了因操作被部分执行而导致的错误或不一致。
二、互斥锁(pthread_mutex_t)
1.申请锁的过程必须是原子的
<1>申请成功:继续向后运行,访问临界区的代码,访问临界资源
<2>申请失败:阻塞挂起,申请执行流
2.锁提供能力的本质
执行临界区代码,并由并行转为串行
3.锁的原理
<1>硬件级实现,关闭时钟中断
<2>软件级实现:为实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是将寄存器和内存单元的数据进行交换
补:如果把一个变量的内容交换到CPU寄存器内部,本质上是将该变量的内容获取到当前执行流的硬件上下文中
4.锁的作用
最基本的同步机制,确保同一时间只有一个线程持有锁
5.互斥锁既可以用类型pthread_mutex_t来定义,也可以使用函数来定义
<1>初始化锁:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
1)头文件:#include<pthread.h>
2)功能:初始化锁
3)参数:
mutex
是指向互斥锁变量的指针;
attr
是指向互斥锁属性的指针,通常设置为NULL
以使用默认属性
4)返回值:成功返回不为0的值,失败返回0
<2>销毁锁:int pthread_mutex_destroy(pthread_mutex_t *mutex);
1)头文件:#include<pthread.h>
2)功能:销毁锁
3)参数:mutex
是指向互斥锁变量的指针;
4)返回值:成功返回不为0的值,失败返回0
<3>加锁:int pthread_mutex_lock(pthread_mutex_t *mutex);
注:加锁时尽量让范围颗粒度尽可能的细,尽量不要包含太多的非临界区
1)头文件:#include<pthread.h>
2)功能:加锁
3)参数:mutex
是指向互斥锁变量的指针;
4)返回值:成功返回不为0的值,失败返回0
<4>解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex);
1)头文件:#include<pthread.h>
2)功能:解锁
3)参数:mutex
是指向互斥锁变量的指针;
4)返回值:成功返回不为0的值,失败返回0
6.加锁之后,在临界区内部允许线程切换吗?
允许线程切换。由于当前线程并未释放锁,是持锁切换,即使它不在,其他线程也要等它回来执行完代码,释放锁,其他线程才能展开锁的竞争进入临界区
线程同步
一、概念介绍
在多线程编程中,线程同步(Thread Synchronization)是解决多线程并发执行时共享资源访问冲突的核心机制。其核心目标是确保多个线程在访问共享数据或执行关键操作时能够按照预期的顺序和规则进行,避免数据竞争(Data Race)、死锁(Deadlock)等并发问题。
二、cond接口
1.int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
<1>头文件:#include<pthread.h>
<2>功能:初始化条件变量
<3>参数:
cond
:指向条件变量的指针;
attr
:条件变量属性,通常设为NULL
使用默认属性。
<4>返回值:成功返回不为0的值,失败返回0
2.int pthread_cond_destroy(pthread_cond_t *cond);
<1>头文件:#include<pthread.h>
<2>功能:销毁条件变量,释放相应资源
<3>参数:cond
:指向条件变量的指针;
<4>返回值:成功返回不为0的值,失败返回0
3.int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
<1>头文件:#include<pthread.h>
<2>功能:阻塞当前线程,直至被其他线程唤醒
<3>参数:
cond
:指向条件变量的指针;
mutex
:与条件变量关联的互斥锁(必须在调用前已加锁)。
<4>返回值:成功返回不为0的值,失败返回0
4.int pthread_cond_signal(pthread_cond_t *cond);
<1>头文件:#include<pthread.h>
<2>功能:唤醒一个在等待在条件变量上的线程
<3>参数:cond
:指向条件变量的指针;
<4>返回值:成功返回不为0的值,失败返回0
5.int pthread_cond_broadcast(pthread_cond_t *cond);
<1>头文件:#include<pthread.h>
<2>功能:唤醒所有在等待在条件变量上的线程
<3>参数:cond
:指向条件变量的指针;
<4>返回值:成功返回不为0的值,失败返回0
三、生产者与消费者模型(“321”原则)
1.3种关系:
生产者之间:竞争关系/互斥关系;
消费者之间:竞争关系/互斥关系;
生产者和消费者之间:互斥关系,同步关系
2.2种角色:生产者和消费者(由线程承担)
3.1个交易场所(一种临界资源):以特定结构构成的一个“内存”空间
4.为什么要有生产者与消费者模型?/有什么好处?
<1>生产过程和消费过程解耦
<2>支持忙闲不均
<3>提高效率(体现在为获取任务和处理任务时是并发的)
5.实例介绍:基于阻塞队列(blockqueue)的生产者与消费者模型
仍遵循“先进先出”的原则,但是当队列为空时从队列中获取元素时会被阻塞,直至队列中被放入元素
6.代码呈现
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE];
int count = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void* producer(void* arg) {for (int i = 0; i < 20; i++) {pthread_mutex_lock(&mutex);while (count == BUFFER_SIZE) {pthread_cond_wait(&cond, &mutex); // 缓冲区满,等待}buffer[count++] = i;printf("Produced: %d\n", i);pthread_cond_signal(&cond); // 通知消费者pthread_mutex_unlock(&mutex);}return NULL;
}void* consumer(void* arg) {for (int i = 0; i < 20; i++) {pthread_mutex_lock(&mutex);while (count == 0) {pthread_cond_wait(&cond, &mutex); // 缓冲区空,等待}int item = buffer[--count];printf("Consumed: %d\n", item);pthread_cond_signal(&cond); // 通知生产者pthread_mutex_unlock(&mutex);}return NULL;
}int main() {pthread_t prod, cons;pthread_create(&prod, NULL, producer, NULL);pthread_create(&cons, NULL, consumer, NULL);pthread_join(prod, NULL);pthread_join(cons, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}