当前位置: 首页 > news >正文

线程互斥量和信号量的使用(未完成)

线程互斥量和信号量的使用

  • 信号量和互斥量的介绍
    • 1. 线程进入阻塞状态
    • 2. 线程收到信号
    • 3. 线程竞争互斥锁
      • 1.初始化:
      • 2.生产者线程:
      • 3.消费者线程:
      • 4.销毁:
  • 疑问1:发送信号量和解锁顺序可以调整吗
    • 答:不推荐,在释放锁和唤醒线程之间,可能会有其他线程修改共享资源的状态,导致被唤醒的线程检查条件时条件不满足,从而再次进入等待状态。

信号量和互斥量的介绍

总结:信号量的参于带来线程阻塞到就绪状态,当等待的线程收到信号后,先变为就绪状态,争抢到互斥量后(能够上锁后),才能够进入运行态

在使用条件变量和互斥锁进行线程同步时,带有条件变量等待的线程会经历一系列状态变化,从阻塞开始,到收到信号,再到获取互斥量。下面详细介绍这些状态变化。

1. 线程进入阻塞状态

当线程调用 pthread_cond_wait 函数时,它会执行以下操作:
释放互斥锁:pthread_cond_wait 函数会自动释放当前线程持有的互斥锁,这样其他线程就可以获取该互斥锁并访问共享资源。
进入阻塞状态:线程会进入条件变量的等待队列,处于阻塞状态,等待其他线程发送信号。

2. 线程收到信号

当其他线程调用 pthread_cond_signal(唤醒一个等待的线程)或 pthread_cond_broadcast(唤醒所有等待的线程)函数时,等待队列中的线程会收到信号。收到信号后,线程会从阻塞状态变为就绪状态,但此时它还没有获取互斥锁,不能立即继续执行。

3. 线程竞争互斥锁

收到信号的线程进入就绪状态后,会参与互斥锁的竞争。具体情况如下:
互斥锁未被持有:如果此时互斥锁没有被其他线程持有,收到信号的线程可以立即获取互斥锁,从就绪状态变为运行状态,继续执行 pthread_cond_wait 函数之后的代码。
互斥锁被其他线程持有:如果互斥锁被其他线程持有,收到信号的线程会再次进入阻塞状态,等待互斥锁被释放。当互斥锁被释放后,该线程会与其他等待该互斥锁的线程一起竞争,竞争成功后才能获取互斥锁并继续执行。
示例代码及状态变化说明

1.初始化:

在 main 函数中,对互斥锁和条件变量进行初始化。

2.生产者线程:

在 producer 函数中,线程先加锁,检查缓冲区是否已满,若已满则调用 pthread_cond_wait 函数阻塞。生产数据后,调用 pthread_cond_signal 函数唤醒等待的消费者线程,最后解锁。

3.消费者线程:

在 consumer 函数中,线程先加锁,检查缓冲区是否为空,若为空则调用 pthread_cond_wait 函数阻塞。消费数据后,调用 pthread_cond_signal 函数唤醒等待的生产者线程,最后解锁。

4.销毁:

在 main 函数结束前,销毁互斥锁和条件变量。
下面是之前生产者 - 消费者模型的示例代码,结合代码说明线程的状态变化:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

#define BUFFER_SIZE 5

// 缓冲区
int buffer[BUFFER_SIZE];
int count = 0;
int in = 0;
int out = 0;

// 互斥锁和条件变量
pthread_mutex_t mutex;
pthread_cond_t not_full;
pthread_cond_t not_empty;

// 生产者线程函数
void *producer(void *arg) {
    for (int i = 0; i < 10; i++) {
        // 加锁
        pthread_mutex_lock(&mutex);

        // 等待缓冲区不满
        while (count == BUFFER_SIZE) {
            pthread_cond_wait(&not_full, &mutex);
        }

        // 生产数据
        buffer[in] = i;
        printf("Produced: %d\n", buffer[in]);
        in = (in + 1) % BUFFER_SIZE;
        count++;

        // 唤醒等待的消费者线程
        pthread_cond_signal(&not_empty);

        // 解锁
        pthread_mutex_unlock(&mutex);

        // 模拟生产时间
        sleep(1);
    }
    return NULL;
}

// 消费者线程函数
void *consumer(void *arg) {
    for (int i = 0; i < 10; i++) {
        // 加锁
        pthread_mutex_lock(&mutex);

        // 等待缓冲区不为空
        while (count == 0) {
            pthread_cond_wait(&not_empty, &mutex);
        }

        // 消费数据
        int data = buffer[out];
        printf("Consumed: %d\n", data);
        out = (out + 1) % BUFFER_SIZE;
        count--;

        // 唤醒等待的生产者线程
        pthread_cond_signal(&not_full);

        // 解锁
        pthread_mutex_unlock(&mutex);

        // 模拟消费时间
        sleep(1);
    }
    return NULL;
}

int main() {
    pthread_t producer_thread, consumer_thread;

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&not_full, NULL);
    pthread_cond_init(&not_empty, 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(&not_full);
    pthread_cond_destroy(&not_empty);

    return 0;
}    

疑问1:发送信号量和解锁顺序可以调整吗

//唤醒等待的消费者线程
pthread_cond_signal(&not_empty);
//解锁
pthread_mutex_unlock(&mutex);
以上顺序可以调换吗

答:不推荐,在释放锁和唤醒线程之间,可能会有其他线程修改共享资源的状态,导致被唤醒的线程检查条件时条件不满足,从而再次进入等待状态。

在大多数情况下,pthread_cond_signal 和 pthread_mutex_unlock 的调用顺序不建议调换,即通常应该先调用 pthread_cond_signal 唤醒等待的线程,再调用 pthread_mutex_unlock 解锁,下面详细解释原因。
原顺序(先 pthread_cond_signal 后 pthread_mutex_unlock)
原代码的顺序如下:

// 唤醒等待的消费者线程
pthread_cond_signal(&not_empty);

// 解锁
pthread_mutex_unlock(&mutex);

这种顺序是合理且推荐的,原因如下:
减少线程上下文切换开销:当调用 pthread_cond_signal 时,被唤醒的线程会进入就绪状态,但由于互斥锁仍被当前线程持有,被唤醒的线程会阻塞在互斥锁上。之后当前线程释放锁,被唤醒的线程能立即获取锁并继续执行,减少了不必要的线程上下文切换。
避免竞态条件:确保被唤醒的线程能在锁被释放后立即获得锁,避免其他线程在释放锁和唤醒线程之间获取锁,从而减少竞态条件的发生。
调换顺序(先 pthread_mutex_unlock 后 pthread_cond_signal)
若将顺序调换,代码如下:

// 解锁
pthread_mutex_unlock(&mutex);

// 唤醒等待的消费者线程
pthread_cond_signal(&not_empty);

这种顺序可能会导致以下问题:
增加线程上下文切换开销:释放锁后,其他等待该锁的线程可能会立即获取锁。当调用 pthread_cond_signal 唤醒等待条件变量的线程时,被唤醒的线程可能需要再次等待锁的释放,增加了线程上下文切换的次数。
潜在的竞态条件:在释放锁和唤醒线程之间,可能会有其他线程修改共享资源的状态,导致被唤醒的线程检查条件时条件不满足,从而再次进入等待状态。

相关文章:

  • 基于SpringBoot的社区/物业管理系统
  • Vala编程语言教程-语言元素
  • Netty源码—2.Reactor线程模型一
  • Microchip AN1477中关于LLC数字补偿器的疑问
  • Python列表2
  • JAVA学习-练习试用Java实现“编写一个Spark程序,结合Elasticsearch对大数据进行全文搜索和筛选“
  • 利用ffmpeg库实现音频AAC编解码
  • 车载以太网网络测试-16【传输层-UDP】
  • 让“树和二叉树”埋在记忆土壤中--性质和概念
  • 服务器数据恢复—服务器raid故障导致上层分区不可用的数据恢复案例
  • 【AI工具】试用秘塔AI搜索的“生成互动网页”功能
  • Linux内核IPv4路由选择子系统
  • 【一起来学kubernetes】21、Secret使用详解
  • 分享:图片识别改名,能识别图片中的文字并批量改名的工具,用WPF和阿里云来完成
  • 如何通过 SQLyog 连接远程 MySQL 数据库?(附工具下载)
  • Web-Machine-N7靶机攻略
  • 【高项】信息系统项目管理师(九)项目资源管理【4分】
  • Android11至15系统定制篇
  • wow-rag—task5:流式部署
  • Java 推送钉钉应用消息
  • 复旦大学与上海杨浦共建市东医院
  • AI观察|从万元到百万元,DeepSeek一体机江湖混战
  • 93岁南开退休教授陈生玺逝世,代表作《明清易代史独见》多次再版
  • 商务部就开展打击战略矿产走私出口专项行动应询答记者问
  • 媒体谈法院就“行人相撞案”道歉:执法公正,普法莫拉开“距离”
  • 印方称所有敌对行动均得到反击和回应,不会升级冲突