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

Linux编程:9、线程编程-互斥锁与条件变量

一、互斥量(互斥锁)

1. 概述
  • 功能:在功能上类似初值为 1 的信号量,通过加锁和解锁操作,实现对临界区的互斥访问。
  • 适用场景:多线程需要共享资源时,防止多个线程同时操作共享资源导致的数据不一致问题。
2. 相关接口
接口头文件功能参数返回值
pthread_mutex_init#include <pthread.h>以动态方式初始化互斥锁(初始化后为未锁定状态)参数 1:指向待初始化的互斥量;参数 2:互斥锁属性(为 NULL 时使用默认属性)成功返回 0;失败返回非零值
pthread_mutex_lock#include <pthread.h>对指定互斥锁加锁(若已加锁,当前线程阻塞)待加锁的互斥锁成功返回 0;失败返回错误代码
pthread_mutex_unlock#include <pthread.h>对指定互斥锁解锁待解锁的互斥锁成功返回 0;失败返回错误代码
pthread_mutex_destroy#include <pthread.h>销毁指定互斥锁待销毁的互斥锁成功返回 0;失败返回错误代码
  • 初始化方式:
    • 动态初始化:使用pthread_mutex_init函数。
    • 静态初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
3. 实例:多线程购票问题
  • 问题:多线程并发购票时,可能出现重复购票等数据不一致问题。
  • 解决方案:使用互斥锁确保同一时间只有一个线程操作票数变量。
  • 关键代码逻辑:
    • 定义全局互斥锁pthread_mutex_t mutex;
    • 主线程中初始化互斥锁:pthread_mutex_init(&mutex, NULL);
    • 购票线程中,操作票数前加锁pthread_mutex_lock(&mutex);,操作完成后解锁pthread_mutex_unlock(&mutex);
    • 线程结束后销毁互斥锁:pthread_mutex_destroy(&mutex);
  • 未加锁的代码:
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>int ticketCount = 100;struct info
    {char name[64];int  count;
    };void* buyThread(void* arg)
    {struct info* info = (struct info*)arg;printf("%s 买票线程启动, 准备买 %d 张票\n", info->name, info->count);for (int i = 0; i < info->count; i++) {printf("当前剩余: %d 张票,%s 要买一张票\n", ticketCount, info->name);sleep(1);ticketCount--;sleep(1);}// 线程结束,返回指定的值pthread_exit((void*)"购票成功!");
    }int main()
    {// 创建一个新线程(子线程)pthread_t   thread1;struct info man1 = {"小明", 3};int         ret  = pthread_create(&thread1, NULL, buyThread, (void*)&man1);if (ret) {  // ret != 0perror("线程创建失败");exit(EXIT_FAILURE);}pthread_t   thread2;struct info man2 = {"小红", 3};ret              = pthread_create(&thread2, NULL, buyThread, (void*)&man2);if (ret) {  // ret != 0perror("线程创建失败");exit(EXIT_FAILURE);}printf("等待买票线程结束...\n");// 等待指定的线程结束void* thread_result;ret = pthread_join(thread1, &thread_result);if (ret) {perror("Thread join failed");exit(1);}printf("线程[%p] 结束, 返回值是: %s\n", thread1, (char*)thread_result);ret = pthread_join(thread2, &thread_result);if (ret) {perror("Thread join failed");exit(1);}printf("线程[%p] 结束, 返回值是: %s\n", thread2, (char*)thread_result);printf("\n 现在总票数是: %d\n", ticketCount);return 0;
    }
  • 加锁的代码:
    #include <cstdlib>
    #include <pthread.h>
    #include <cstring>
    #include <cstdio>
    #include <unistd.h>
    #include <ctime>int ticketCount = 100;// 定义一个互斥锁
    pthread_mutex_t mutex;struct info
    {char name[64];int  count;
    };void* buyThread(void* arg)
    {struct info* info = (struct info*)arg;printf("%s 买票线程启动, 准备买 %d 张票\n", info->name, info->count);for (int i = 0; i < info->count; i++) {int seconds = 1 + rand() % 3;sleep(seconds);// 进入临界区要加锁pthread_mutex_lock(&mutex);printf("当前剩余: %d 张票,%s 要买一张票\n", ticketCount, info->name);ticketCount--;// 出了临界区解锁pthread_mutex_unlock(&mutex);}// 线程结束,返回指定的值pthread_exit((void*)"购票成功!");
    }int main()
    {srand(time(0));// 初始化锁int ret = pthread_mutex_init(&mutex, NULL);if(ret != 0) {perror("mutex init failed");exit(1);}// 创建一个新线程(子线程)pthread_t   thread1;struct info man1 = {"小明", 3};ret  = pthread_create(&thread1, NULL, buyThread, (void*)&man1);if (ret) {  // ret != 0perror("线程创建失败");exit(EXIT_FAILURE);}pthread_t   thread2;struct info man2 = {"小红", 3};ret              = pthread_create(&thread2, NULL, buyThread, (void*)&man2);if (ret) {  // ret != 0perror("线程创建失败");exit(EXIT_FAILURE);}printf("等待买票线程结束...\n");// 等待指定的线程结束void* thread_result;ret = pthread_join(thread1, &thread_result);if (ret) {perror("Thread join failed");exit(1);}printf("线程[%p] 结束, 返回值是: %s\n", thread1, (char*)thread_result);ret = pthread_join(thread2, &thread_result);if (ret) {perror("Thread join failed");exit(1);}printf("线程[%p] 结束, 返回值是: %s\n", thread2, (char*)thread_result);printf("\n 现在总票数是: %d\n", ticketCount);// 用完之后销毁互斥锁pthread_mutex_destroy(&mutex);return 0;
    }


二、条件变量

1. 概述
  • 适用场景:存在多个生产者和消费者,资源个数不确定;消费者 / 生产者线程需等待特定条件才能进行操作。
  • 核心:线程通过等待或发送信号,基于特定条件实现同步。
2. 相关接口
接口头文件功能参数返回值
pthread_cond_init#include <pthread.h>初始化条件变量参数 1:待初始化的条件变量;参数 2:条件变量属性(为 NULL 时使用默认属性)成功返回 0;失败返回错误码
pthread_cond_wait#include <pthread.h>使线程等待条件变量(需配合互斥锁使用)参数 1:指向条件变量的指针;参数 2:指向互斥锁的指针(调用前需由当前线程锁定)成功返回 0;失败返回错误码
pthread_cond_signal#include <pthread.h>唤醒等待特定条件变量的一个线程指向要发送信号的条件变量成功返回 0;失败返回错误码
pthread_cond_broadcast#include <pthread.h>唤醒所有等待指定条件变量的线程指向要广播信号的条件变量成功返回 0;失败返回错误码
pthread_cond_destroy#include <pthread.h>销毁指定条件变量待销毁的条件变量-

  • 初始化方式:

    • 动态初始化:使用pthread_cond_init函数。
    • 静态初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  • pthread_cond_wait执行过程:

    1. 解锁当前持有的互斥锁,允许其他线程获取锁操作共享资源。
    2. 将当前线程加入与条件变量关联的等待队列,进入阻塞状态。
    3. 等待被pthread_cond_signalpthread_cond_broadcast唤醒。
    4. 被唤醒后,重新获取之前释放的互斥锁(若锁被其他线程持有,当前线程会阻塞等待)。
  • 注意:pthread_cond_wait返回后需重新检查条件(通常用while循环),因条件可能已变化。

3. 实例:生产者 - 消费者模型
  • 场景:多个生产者线程生产数据,多个消费者线程消费数据,共享资源为shared_data
  • 关键代码逻辑:
    • 定义全局条件变量pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;和互斥锁pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    • 生产者线程:锁定互斥锁→生产数据→调用pthread_cond_signal唤醒消费者→解锁互斥锁。
    • 消费者线程:锁定互斥锁→while循环检查条件(shared_data == 0时调用pthread_cond_wait等待)→消费数据→解锁互斥锁。
    • 线程结束后销毁条件变量和互斥锁:pthread_cond_destroy(&cond_var);pthread_mutex_destroy(&mutex);
  • 示例代码:
    #include <pthread.h>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <unistd.h>
    #include <ctime>#define PRODUCER_THREAD_NUM 5
    #define CONSUMER_THREAD_NUM 5pthread_cond_t cond_var = PTHREAD_COND_INITIALIZER;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 共享数据
    int shared_data = 0;void* producer_func(void* args)
    {char* producerName = static_cast<char*>(args);while(1) {sleep(rand() % 3 + 1);  // 模拟生产耗时pthread_mutex_lock(&mutex);shared_data++;printf("%s 生产了一个产品,剩余产品数量: %d\n", producerName, shared_data);pthread_cond_signal(&cond_var); // 唤醒一个等待的消费者线程pthread_mutex_unlock(&mutex);}pthread_exit(NULL);
    }void* consumer_func(void* args) {char* consumerName = static_cast<char*>(args);while(1) {sleep(rand() % 3 + 1);  // 模拟消费耗时pthread_mutex_lock(&mutex);while(shared_data == 0) {// 如果资源等于 0,等待成品生产pthread_cond_wait(&cond_var, &mutex);}shared_data--;printf("%s 消耗了一个产品,剩余产品数量: %d\n", consumerName,shared_data);pthread_mutex_unlock(&mutex);}pthread_exit(NULL);
    }int main()
    {srand(time(NULL));pthread_t producers[PRODUCER_THREAD_NUM];pthread_t consumers[CONSUMER_THREAD_NUM];for(int i = 0; i < PRODUCER_THREAD_NUM; i++) {char* buff = new char[256];sprintf(buff, "第 %d 号生产者线程", i + 1);int ret = pthread_create(&producers[i], NULL,producer_func, buff);if(ret != 0) {perror("pthread create failed");exit(1);}}for (int i = 0; i < CONSUMER_THREAD_NUM; i++) {char* buff = new char[256];sprintf(buff, "第 %d 号消费者线程", i + 1);int ret = pthread_create(&consumers[i], NULL,consumer_func, buff);if (ret != 0) {perror("pthread create failed");exit(1);}}for (int i = 0; i < PRODUCER_THREAD_NUM; i++) {pthread_join(producers[i], NULL);}for (int i = 0; i < PRODUCER_THREAD_NUM; i++){pthread_join(consumers[i], NULL);}pthread_cond_destroy(&cond_var);pthread_mutex_destroy(&mutex);return 0;
    }


三、线程的属性

1. 概述
  • 线程属性用于设置线程的特性(如脱离状态、调度策略等),通过pthread_attr_t类型变量管理。
2. 基本接口
  • pthread_attr_init:初始化线程属性,参数为待初始化的线程属性,成功返回 0,失败返回错误码。
  • pthread_attr_destroy:销毁线程属性(销毁后不影响已创建的线程,可重新初始化),参数为待销毁的线程属性,成功返回 0,失败返回错误码。
3. 主要属性
  • 脱离状态属性(pthread_attr_setdetachstate):

    • 功能:设置线程是否可被pthread_join等待。
    • 参数 2 可选值:
      • PTHREAD_CREATE_DETACHED:线程为脱离状态,原线程不能用pthread_join等待其结束。
      • PTHREAD_CREATE_JOINABLE(默认):原线程可用pthread_join等待其结束。
  • 调度策略属性(pthread_attr_setschedpolicy):

    • 功能:设置线程的调度策略。
    • 参数 2 可选值:
      • SCHED_OTHER:默认策略,由操作系统根据负载和优先级自动调度。
      • SCHED_FIFO:先进先出策略(需超级用户权限),同优先级线程按顺序执行,高优先级线程可抢占低优先级线程。
      • SCHED_RR:轮转调度策略,同优先级线程分时运行(获得时间片),高优先级线程可抢占低优先级线程。
  • 优先级设置:通过pthread_attr_setschedparam设置,需结合调度策略,可通过sched_get_priority_maxsched_get_priority_min获取优先级范围。

4. 实例:设置线程属性
  • 关键代码逻辑:
    • 定义线程属性变量pthread_attr_t thread_attr;
    • 初始化属性:pthread_attr_init(&thread_attr);
    • 设置脱离状态:pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
    • 设置调度策略:pthread_attr_setschedpolicy(&thread_attr, SCHED_OTHER);
    • 设置优先级:通过sched_param结构体配置,调用pthread_attr_setschedparam
    • 创建线程:pthread_create(&a_thread, &thread_attr, thread_function, (void*)n);
    • 销毁属性:pthread_attr_destroy(&thread_attr);
  • 示例代码:
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdint.h>int   finished = 0;void* thread_function(void* arg)
    {printf("线程开始运行...");int count = static_cast<int>(reinterpret_cast<intptr_t>(arg));for (int i = 0; i < count; i++) {printf("线程正在工作,第%d 次\n", i + 1);sleep(1);}printf("线程即将关闭.\n");finished = 1;pthread_exit(NULL);
    }int main()
    {// 定义一个线程属性pthread_attr_t thread_attr;// 初始化线程属性int res = pthread_attr_init(&thread_attr);if (res != 0) {perror("pthread_attr_init failed");exit(1);}// 设置线程的脱离状态属性res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);if (res != 0) {perror("Setting detached attribute failed");exit(EXIT_FAILURE);}// 设置调度策略//  1. SCHED_OTHER//  默认的调度策略//  由操作系统自动选择线程运行顺序。//  操作系统会根据系统的负载和线程的优先级自动调度线程的执行//  2. SCHED_FIFO//  使用先进先出策略,需要以超级用户的权限启动进程//  同优先级的实时进程,后进入的进程要等前一个进程释放了 CPU,才能够运行//  不同优先级的实时进程,高优先级的实时进程能够抢占低优先级的实时进程//  3. SCHED_RR//  轮转调度策略(Round-Robin)//  同优先级的实时进程中,每个进程又是通过获得时间片来分时运行//  不同优先级的实时进程,高优先级的实时进程能够抢占低优先级的实时进程res = pthread_attr_setschedpolicy(&thread_attr, SCHED_OTHER);if (res != 0) {perror("pthread_attr_setschedpolicy failed.");exit(1);}// 获取优先级范围int max_priority = sched_get_priority_max(SCHED_OTHER);int min_priority = sched_get_priority_min(SCHED_OTHER);// 设置优先级struct sched_param scheduling_value;scheduling_value.sched_priority = min_priority;res = pthread_attr_setschedparam(&thread_attr, &scheduling_value);if (res != 0) {perror("pthread_attr_setschedparam failed.");exit(1);}// 创建线程pthread_t a_thread;int       n = 3;res = pthread_create(&a_thread, &thread_attr,thread_function, reinterpret_cast<void*>(static_cast<intptr_t>(n)));if (res != 0) {perror("Thread creation failed");exit(EXIT_FAILURE);}// 线程属性不再需要时,可以使用 pthread_attr_destroy 进行销毁// 线程属性销毁后,对使用这个属性创建的线程没有影响// 线程属性销毁后,可以重新进行初始化pthread_attr_destroy(&thread_attr);// 已经不能使用 pthread_join 来等待线程结束了// 这里使用共享变量来判断while (!finished) {printf("等待线程结束...\n");sleep(1);}printf("线程已结束\n");return 0;
    }


四、线程的取消

1. 概述
  • 场景:线程 A 需让线程 B 停止运行时,向线程 B 发送取消请求,线程 B 根据自身 “取消状态” 和 “取消类型” 决定是否及如何取消执行。
2. 相关接口
接口头文件功能参数返回值
pthread_setcancelstate#include <pthread.h>设置线程的取消状态参数 1:PTHREAD_CANCEL_ENABLE(允许接收取消请求)或PTHREAD_CANCEL_DISABLE(禁止接收);参数 2:获取之前的取消状态(可为 NULL)成功返回 0;失败返回错误码
pthread_setcanceltype#include <pthread.h>设置线程的取消类型(允许取消时生效)参数 1:PTHREAD_CANCEL_DEFERRED(收到请求后,执行 “取消点” 函数时取消)或PTHREAD_CANCEL_ASYNCHRONOUS(收到请求后立即取消);参数 2:获取之前的取消类型成功返回 0;失败返回错误码
pthread_cancel#include <pthread.h>向指定线程发送取消请求目标线程的标识符成功返回 0;失败返回错误码
http://www.dtcms.com/a/295063.html

相关文章:

  • 扫地机产品的电池CQC认证遵循哪个标准?
  • 1. 一份“从 0 到 1” 的 WSL(Windows Subsystem for Linux)速查手册
  • J2EE模式---视图助手模式
  • ospf多区域
  • git的使用,推送仓库github
  • Hierarchical-Localization 安装与常见问题解决手册
  • MSTP多生成树协议
  • 【西北工业大学公开课】导引系统原理(全61讲)周军 -个人笔记版 5000字
  • 基于多种机器学习的水质污染及安全预测分析系统的设计与实现【随机森林、XGBoost、LightGBM、SMOTE、贝叶斯优化】
  • Parasoft为金融服务打造统一测试平台,提升安全、合规与交付效率
  • Shell函数
  • 无人设备遥控器之无线网络技术篇
  • Linux 一文详谈Vim编辑器的使用
  • 面试150 最大子数组和
  • C语言学习(days09)
  • useEffect
  • Java异常处理核心原理与最佳实践
  • 数据驱动未来:构建强大AI系统的基石
  • QPixmap::scaled参数说明
  • 床上肢体康复机器人的机械结构设计cad【7张】三维图+设计说明书
  • 1、黑马点评复盘(短信登录-Session或Redis实现)
  • pytest简单使用和生成测试报告
  • FCW(Front Collision Warning)前碰撞预警功能介绍
  • 借助DataStream和多路复用实现可观察性
  • mybatis条件语句的查询与注解的使用以及mybatis与servelet结合查询
  • 数据结构系列之AVL树
  • 主要科技公司与新创公司 AI Agent 进展调研
  • Nginx 日志分析与慢请求排查
  • Symantec sep配置自定义yara规则
  • 背包九讲 详细解析与 C++ 实现