1、思维导图
2、互斥锁
1.互斥锁实现互斥的代码
3.防死锁
默认防死锁 trylock(不推荐,容易破环互斥的同步性)
常用防死锁的方式有——递归锁、错误检查锁
函数原型:int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
功能描述:将互斥锁属性attr,设置成kind类型
参数描述:
参数 attr:互斥锁属性地址
参数 kind:互斥锁到底是默认的快速锁,还是递归锁,还是错误检查锁,全都取决于这个kind
PTHREAD_MUTEX_FAST_NP:快速锁
PTHREAD_MUTEX_RECURSIVE_NP:递归锁
PTHREAD_MUTEX_ERRORCHECK_NP:错误检查锁
3.1 默认的普通互斥锁:trylock
函数原型:int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能描述:尝试上锁互斥锁,如果该互斥锁已经上锁,则会上锁失败。无论互斥锁被任意线 程上锁,trylock都不会阻塞返回值:成功上锁返回0,失败上锁返回错误码
所以trylock虽然能够解决死锁问题,但是trylock容易破坏互斥和同步性
3.2递归锁互斥代码
递归锁的行为模式:
当一把递归锁已经上锁的状态下,再次去上锁的话,lock函数会立刻返回,
并且记录该线程上锁了几次这个递归锁
如果该线程想要解锁的话,就必须解锁对应次数的递归锁
lock一把已经上锁的递归锁能够不阻塞并立刻返回的前提条件是:
①多次lock行为必须发生在同一个线程
②不同线程的lock行为依旧会阻塞
静态创建递归锁
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
动态创建递归锁
第一步:创建互斥锁
pthread_mutex_t m;
第二步:创建互斥锁属性
pthread_mutexattr_t attr;
第三步:初始化互斥锁属性
函数原型:int pthread_mutexattr_init(pthread_mutexattr_t *attr);
功能描述:初始化互斥锁的属性
返回值:总是返回0
调用形式:
pthread_mutexattr_init(&attr)
第四步:将互斥锁的属性,设置为递归锁
函数原型:int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
功能描述:将互斥锁属性attr,设置成kind类型
参数描述:
参数 attr:互斥锁属性地址
参数 kind:锁类型 如下3个
PTHREAD_MUTEX_FAST_NP:快速锁
PTHREAD_MUTEX_RECURSIVE_NP:递归锁
PTHREAD_MUTEX_ERRORCHECK_NP:错误检查锁
调用形式:
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP)
3.3 错误检查锁互斥代码
错误检查锁的行为模式:
当一把已经上锁的互斥锁,再次上锁的时候,lock函数立刻返回错误码
错误检查锁和递归锁一样的,仅当同一线程,反复lock错误检查锁的情况下,多出来的lock才会立刻返回错误码 其他线程的lock正常阻塞
静态创建错误检查锁:
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
动态创建步骤和 递归锁 前3步 一致,仅第4步 锁类型 属性设置不同
函数原型:int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
参数 attr:互斥锁属性地址
参数 kind:锁类型 如下3个
PTHREAD_MUTEX_FAST_NP:快速锁
PTHREAD_MUTEX_RECURSIVE_NP:递归锁
PTHREAD_MUTEX_ERRORCHECK_NP:错误检查锁pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ERRORCHECK_NP)
4.信号量
4.1信号量互斥代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
int TV_channel = 1;
sem_t s;
void task1(){
// 分支线程
while(1){
sem_wait(&s); // 获取遥控器
TV_channel = 5;
sem_post(&s); // 释放遥控器
}
}
void task2(){
// 主线程
while(1){
sem_wait(&s); // 获取遥控器
TV_channel = 100;
usleep(10);
printf("电视频道 = %d\n",TV_channel);// 电视看完了
sem_post(&s); // 获取遥控器
}
}
void* thread_main(void* arg){
task1();
}
int main(int argc, const char *argv[])
{
sem_init(&s,0,1);
pthread_t id;
pthread_create(&id,0,thread_main,0);
pthread_detach(id);
task2();
return 0;
}
4.2信号量同步代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
sem_t s0;
sem_t s1;
sem_t s2;
void task_main(){
while(1){
sem_wait(&s0);//尝试获取遥控器
printf("主线程看电视\n");// 到这里的时候s0 已经没有遥控器了
sleep(1);
sem_post(&s1);// 给 s1 增加一个遥控器
}
}
void task1(){
while(1){
sem_wait(&s1);//尝试获取遥控器
printf("1#线程看电视\n");
sleep(1);
sem_post(&s2);
}
}
void task2(){
while(1){
sem_wait(&s2);//尝试获取遥控器
printf("2#线程看电视\n");
sleep(1);
sem_post(&s0);
}
}
void* thread_main_1(void* arg){
task1();
}
void* thread_main_2(void* arg){
task2();
}
int main(int argc, const char *argv[])
{
sem_init(&s0,0,1);
sem_init(&s1,0,0);
sem_init(&s2,0,0);
pthread_t id;
pthread_create(&id,0,thread_main_1,0);
pthread_detach(id);
pthread_t id2;
pthread_create(&id2,0,thread_main_2,0);
pthread_detach(id2);
task_main();
return 0;
}
5.条件变量
5.1 创建条件变量和互斥锁变量
pthread_mutex_t mutex;
pthread_cond_t cond;
5.2 条件变量初始化
静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
动态初始化
函数原型:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
功能描述:初始化一个条件变量
参数描述:
参数 cond:条件变量的地址
参数 attr:条件变量属性 这里只能写 NULL
返回值:成功返回0,失败返回错误码
调用形式:
pthread_mutex_t mutex;
pthread_cond_t cond;
pthread_cond_init(&cond,NULL);
5.3询问运行条件是否满足
函数原型:int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);
功能描述:询问运行条件是否满足
注意事项:pthread_cond_wait 这个函数会做3件事情
1:解锁mutex互斥锁
2:等待回复条件满足
3:重新运行当前线程之前,会重新上锁mutex互斥锁
参数描述:
参数 cond: 准备等待的条件变量地址
参数 mutex:想要解锁的互斥锁的地址
返回值:成功返回0,失败返回错误码调用形式:
pthread_cond_wait(&cond,&mutex)
5.4 唤醒wait函数的等待
函数原型:int pthread_cond_signal (pthread_cond_t *cond);
功能描述:唤醒一个 pthread_cond_wait 函数的等待
注意事项:只会唤醒一个被wait的线程,如果没有线程处于wait当中,什么都不发生。如果多个线程同时处于wait,则只会唤醒一个,但是不确定具体是哪个.
当signal函数被调用时,所有处于静止态的被wait的线程,同时进入就绪态,去抢夺时间片抢到时间片的线程就被唤醒开始运行,没抢到时间片的剩余线程,则重新回到静止态
参数描述:
参数 cond:准备唤醒的条件地址
返回值:成功返回0,失败返回错误码调用形式:
pthread_cond_signal(&cond)
函数原型:int pthread_cond_broadcast (pthread_cond_t *cond);
功能描述:唤醒所有被wait阻塞的线程
注意实现:总是先唤醒先被wait阻塞的线程
参数描述:
参数 cond:准备唤醒的条件地址
返回值:成功返回0,失败返回错误码调用形式:
pthread_cond_broadcast (&cond)
5.5 销毁条件变量
函数原型:int pthread_cond_destroy (pthread_cond_t *cond);
功能描述:销毁条件变量
注意事项:销毁条件变量之前,不能有任何线程被wait阻塞
参数描述:
参数 cond:准备唤醒的条件地址
返回值:成功返回0,失败返回错误码调用形式:
pthread_cond_destroy (&cond)
5.6 条件变量实现互斥
5.7 条件变量实现同步
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
pthread_mutex_t mutex;
pthread_cond_t cond1;
pthread_cond_t cond2;
pthread_cond_t cond3;
void* task1(void* arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond1,&mutex);
printf("线程1唤醒\n");
pthread_mutex_unlock(&mutex);
}
}
void* task2(void* arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond2,&mutex);
printf("线程2唤醒\n");
pthread_mutex_unlock(&mutex);
}
}
void* task3(void* arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond3,&mutex);
printf("线程3唤醒\n");
pthread_mutex_unlock(&mutex);
}
}
int main(int argc, const char *argv[])
{
pthread_t id1,id2,id3;
pthread_create(&id1,0,task1,0);
pthread_create(&id2,0,task2,0);
pthread_create(&id3,0,task3,0);
pthread_detach(id1);
pthread_detach(id2);
pthread_detach(id3);
int ch = 0;
while(1)
{
printf("\n");
printf("1:唤醒1#线程\n");
printf("2:唤醒2#线程\n");
printf("3:唤醒3#线程\n");
printf("4:唤醒所有线程\n");
printf("0:退出线程\n");
printf("请输入:");
scanf("%d",&ch);
while(getchar()!=10);
switch(ch)
{
case 1:
pthread_cond_signal(&cond1);
usleep(100);
break;
case 2:
pthread_cond_signal(&cond2);
usleep(100);
break;
case 3:
pthread_cond_signal(&cond3);
usleep(100);
break;
case 4:
pthread_cond_broadcast(&cond1);
pthread_cond_broadcast(&cond2);
pthread_cond_broadcast(&cond3);
usleep(100);
break;
case 0:
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond1);
pthread_cond_broadcast(&cond2);
pthread_cond_broadcast(&cond3);
pthread_cond_destroy(&cond1);
pthread_cond_destroy(&cond2);
pthread_cond_destroy(&cond3);
pthread_mutex_unlock(&mutex);
pthread_mutex_destroy(&mutex);
exit(0);
}
}
return 0;
}
作业:
1:两车过隧道
有一个隧道,长1000m,有一辆高铁,每秒100米,有一辆快车,每秒50m
要求模拟这两列火车通过隧道的场景
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#define TUNNEL_LENGTH 1000 // 隧道长度
typedef struct {
const char *name; // 火车名称
int speed; // 火车速度(米/秒)
int position; // 火车当前位置
} Train;
void *simulate_train(void *arg) {
Train *train = (Train *)arg;
while (train->position < TUNNEL_LENGTH) {
train->position += train->speed; // 火车前进
if (train->position > TUNNEL_LENGTH)
train->position = TUNNEL_LENGTH; // 防止超出隧道长度
printf("%s 在 %dm\n", train->name, train->position);
sleep(1); // 模拟每秒前进
}
printf("%s has exited the tunnel.\n", train->name);
return NULL;
}
int main() {
pthread_t high_speed_thread, fast_train_thread;
// 初始化两列火车
Train high_speed_train = {"High-Speed Train", 100, 0};
Train fast_train = {"Fast Train", 50, 0};
// 创建线程模拟火车运行
pthread_create(&high_speed_thread, NULL, simulate_train, &high_speed_train);
pthread_create(&fast_train_thread, NULL, simulate_train, &fast_train);
// 等待两列火车完成运行
pthread_join(high_speed_thread, NULL);
pthread_join(fast_train_thread, NULL);
printf("Both trains have exited the tunnel.\n");
return 0;
}
2:三车过隧道
有一个隧道,长1000m,有一辆高铁,每秒100米,有一辆快车,每秒50m,有一辆慢车每秒25m要求模拟这两列火车通过隧道的场景,但是要求高铁最先过隧道,快车其次,慢车最后
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#define TUNNEL_LENGTH 1000 // 隧道长度
typedef struct {
const char *name; // 火车名称
int speed; // 火车速度(米/秒)
int position; // 火车当前位置
int order; // 火车顺序(1:高铁,2:快车,3:慢车)
} Train;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int current_order = 1; // 当前允许进入隧道的火车顺序
void *simulate_train(void *arg) {
Train *train = (Train *)arg;
// 等待轮到当前火车进入隧道
pthread_mutex_lock(&mutex);
while (train->order != current_order) {
pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
// 模拟火车通过隧道
while (train->position < TUNNEL_LENGTH) {
train->position += train->speed; // 火车前进
if (train->position > TUNNEL_LENGTH) {
train->position = TUNNEL_LENGTH; // 防止超出隧道长度
}
printf("%s 在 %dm\n", train->name, train->position);
sleep(1); // 模拟每秒前进
}
printf("%s 已出隧道.\n", train->name);
// 通知下一辆火车
pthread_mutex_lock(&mutex);
current_order++;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
return NULL;
}
int main() {
pthread_t threads[3];
// 初始化三列火车
Train high_speed_train = {"High-Speed Train", 100, 0, 1};
Train fast_train = {"Fast Train", 50, 0, 2};
Train slow_train = {"Slow Train", 25, 0, 3};
// 创建线程模拟火车运行
pthread_create(&threads[0], NULL, simulate_train, &high_speed_train);
pthread_create(&threads[1], NULL, simulate_train, &fast_train);
pthread_create(&threads[2], NULL, simulate_train, &slow_train);
// 等待所有线程完成
for (int i = 0; i < 3; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
3:苹果生产
使用条件变量实现一个生产者消费者模型(pv)模型
生产者线程:每秒生成2个苹果
消费者线程:没3秒消费 5~9个苹果
要求消费者在消费之前一定要有足够的苹果给消费
include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
int apple_count = 0; // 当前苹果数量
int max_apples; // 仓库最大容量
int producer_speed; // 生产者生产速度(每秒生成的苹果数)
int consumer_speed; // 消费者消费速度(每次消费间隔秒数)
pthread_mutex_t mutex; // 互斥锁
pthread_cond_t cond; // 条件变量
// 生产者线程函数
void *producer(void *arg) {
while (1) {
pthread_mutex_lock(&mutex);
// 生产苹果
if (apple_count + producer_speed <= max_apples) {
apple_count += producer_speed;
printf("生产了 %d 苹果. 总数: %d\n", producer_speed, apple_count);
} else {
printf("库存已满...\n");
}
// 唤醒消费者
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
sleep(1); // 模拟每秒生产
}
return NULL;
}
// 消费者线程函数
void *consumer(void *arg) {
while (1) {
pthread_mutex_lock(&mutex);
int consumption = rand() % 5 + 5; // 随机消费 5~9 个苹果
while (apple_count < consumption) {
printf("消费者想要 %d 苹果, 但仅剩 %d 可用.\n", consumption, apple_count);
pthread_cond_wait(&cond, &mutex); // 等待生产者生产
}
// 消费苹果
apple_count -= consumption;
printf("买了 %d 苹果. 剩余: %d\n", consumption, apple_count);
pthread_mutex_unlock(&mutex);
sleep(consumer_speed); // 模拟消费间隔
}
return NULL;
}
int main() {
pthread_t producer_thread, consumer_thread;
// 动态初始化参数
printf("输入最大存储量: ");
scanf("%d", &max_apples);
printf("输入速度 (苹果/秒): ");
scanf("%d", &producer_speed);
printf("输入使用者间隔 (seconds): ");
scanf("%d", &consumer_speed);
// 初始化互斥锁和条件变量
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
// 创建生产者和消费者线程
pthread_create(&producer_thread, NULL, producer, NULL);
pthread_create(&consumer_thread, NULL, consumer, NULL);
// 等待线程完成(实际上是死循环,不会退出)
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
// 销毁互斥锁和条件变量
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
标签:int,内附,互斥,死锁,train,cond,pthread,mutex,include
From: https://blog.csdn.net/weixin_56261190/article/details/145145562