news 2026/4/3 6:05:21

线程同步之互斥量

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
线程同步之互斥量

文章目录

  • 全局变量同步问题
  • 互斥量
    • 互斥量初始化方式
      • 静态初始化(编译时)
      • 动态初始化(运行时)
    • 互斥量操作函数
    • 临界区(Critical Section)
    • 互斥量解决同步问题
      • 注意问题
多线程共享全局变量 → 数据竞争问题 ↓ 需要同步机制 → 互斥量(Mutex)解决方案 ↓ 互斥量使用模式 → 初始化、加锁、解锁、销毁

全局变量同步问题

  • 在多线程程序中,如果多个线程同时对共享的全局变量进行读写操作,可能会出现数据竞争(Race Condition)问题,导致最终结果与预期不一致
/* 为突出问题 代码省略了错误处理*/#include<pthread.h>#include<stdio.h>#include<stdlib.h>staticintglob;// 全局变量,被所有线程共享// 线程函数void*ThreadFunc(void*arg){intloops=*((int*)arg);// 从参数中获取循环次数intloc,j;for(j=0;j<loops;j++){loc=glob;// 读取全局变量loc++;// 局部变量自增glob=loc;// 将局部变量的值赋给全局变量}pthread_exit(NULL);// 线程退出}intmain(intargc,constchar*argv[]){pthread_ttid1,tid2;// 定义两个线程 IDintloops;// 检查命令行参数if(argc!=2){fprintf(stderr,"%s [loops]\n",argv[0]);exit(EXIT_FAILURE);}// 从命令行参数中读取循环次数if(sscanf(argv[1],"%d",&loops)!=1){fprintf(stderr,"Invalid loops\n");exit(EXIT_FAILURE);}// 创建两个线程,都执行 ThreadFunc 函数pthread_create(&tid1,NULL,ThreadFunc,&loops);pthread_create(&tid2,NULL,ThreadFunc,&loops);// 等待两个线程结束pthread_join(tid1,NULL);pthread_join(tid2,NULL);// 打印最终的全局变量值printf("glob = %d\n",glob);return0;}
  • 理论上,如果loops为 n,最终glob的值应该是 2n,但结果似乎并不总是这样

  • 两个线程同时执行上述操作时,可能会出现以下情况:
    • 线程A读取glob值(例如100)
    • 线程B也读取glob(仍为100)
    • 两个线程都执行loc++
    • 最终glob可能只增加一次,而非两次
  • 被称为非原子操作导致的同步问题

互斥量

  • 互斥量(Mutex)是一种锁机制,用于保护共享资源,确保同一时间只有一个线程能访问该资源
  • 属于pthread_mutex_t类型,需在使用前初始化
  • 合理使用互斥量可保证线程安全,提升程序稳定性

互斥量初始化方式

静态初始化(编译时)

  • 对于静态分配的互斥量而言,将PTHREAD_MUTEX_INITIALIZER赋给互斥量
  • 适用于全局变量或静态变量
  • 系统会自动初始化为解锁状态
#include<pthread.h>pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;

静态初始化的方式等效于通过调用pthread_mutex_init()进行动态初始化,并将参数attr指定为NULL,但不会执行错误检查

动态初始化(运行时)

  • 适用于动态分配于堆或栈上的互斥量
    • 动态创建针对某一结构的链表,表中每个结构都包含一个pthread_mutex_t类型的字段来存放互斥量,借以保护对该结构的访问
  • 需要自定义属性
#include<pthread.h>intpthread_mutex_init(pthread_mutex_t*restrict mutex,constpthread_mutexattr_t*restrict attr);
  • mutex:指定函数执行初始化操作的目标互斥量
  • attr:是指向pthread_mutexattr_t类型对象的指针,该对象在函数调用之前已经过了初始化处理,用于定义互斥量的属性。
    • 若将attr参数置为NULL,则该互斥量的各种属性会取默认值

互斥量操作函数

函数说明
pthread_mutex_lock(&mutex)加锁,若已被锁则阻塞
pthread_mutex_unlock(&mutex)解锁
pthread_mutex_trylock(&mutex)尝试加锁,失败立即返回
pthread_mutex_destroy(&mutex)销毁互斥量

临界区(Critical Section)

pthread_mutex_lock(&mutex);// 临界区:访问共享资源的代码glob++;pthread_mutex_unlock(&mutex);

互斥量解决同步问题

  • 静态初始化
#include<pthread.h>#include<stdio.h>#include<stdlib.h>#include<string.h>staticintglob;//临界资源pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;// 静态初始化互斥量void*ThreadFunc(void*arg){intloops=*((int*)arg);intloc,j;for(j=0;j<loops;j++){pthread_mutex_lock(&mutex);//加锁与解锁的代码区域被称为 临界区loc=glob;loc++;glob=loc;pthread_mutex_unlock(&mutex);}pthread_exit(NULL);}intmain(intargc,constchar*argv[]){pthread_ttid1,tid2;intloops;if(argc!=2){fprintf(stderr,"%s [loops]\n",argv[0]);exit(EXIT_FAILURE);}if(sscanf(argv[1],"%d",&loops)!=1){fprintf(stderr,"Invalid loops\n");exit(EXIT_FAILURE);}intret=pthread_create(&tid1,NULL,ThreadFunc,&loops);if(ret!=0){fprintf(stderr,"pthread_create:%s\n",strerror(ret));exit(EXIT_FAILURE);}ret=pthread_create(&tid2,NULL,ThreadFunc,&loops);if(ret!=0){fprintf(stderr,"pthread_create:%s\n",strerror(ret));exit(EXIT_FAILURE);}ret=pthread_join(tid1,NULL);if(ret!=0){fprintf(stderr,"pthread_join tid1:%s\n",strerror(ret));exit(EXIT_FAILURE);}ret=pthread_join(tid2,NULL);if(ret!=0){fprintf(stderr,"pthread_join tid2:%s\n",strerror(ret));exit(EXIT_FAILURE);}printf("glob = %d\n",glob);return0;}
  • 动态初始化
/* 省略了错误处理*/#include<pthread.h>#include<stdio.h>#include<stdlib.h>staticintglob;structthread{intloops;pthread_mutex_tmutex;};// 线程函数void*ThreadFunc(void*arg){structthread*data=(structthread*)arg;// 从参数中获取循环次数intloc,j;for(j=0;j<data->loops;j++){pthread_mutex_lock(&data->mutex);glob++;pthread_mutex_unlock(&data->mutex);}pthread_exit(NULL);// 线程退出}intmain(intargc,constchar*argv[]){pthread_ttid1,tid2;// 定义两个线程 IDstructthreaddata;pthread_mutex_init(&data.mutex,NULL);// 检查命令行参数if(argc!=2){fprintf(stderr,"%s [loops]\n",argv[0]);exit(EXIT_FAILURE);}// 从命令行参数中读取循环次数if(sscanf(argv[1],"%d",&data.loops)!=1){fprintf(stderr,"Invalid loops\n");exit(EXIT_FAILURE);}// 创建两个线程,都执行 ThreadFunc 函数pthread_create(&tid1,NULL,ThreadFunc,&data);pthread_create(&tid2,NULL,ThreadFunc,&data);// 等待两个线程结束pthread_join(tid1,NULL);pthread_join(tid2,NULL);pthread_mutex_destroy(&data.mutex);// 打印最终的全局变量值printf("glob = %d\n",glob);return0;}

注意问题

  • 静态初始化用于全局/静态变量,动态初始化用于局部/动态变量
  • 不要重复初始化已初始化的互斥量
  • 不要在锁定的状态下销毁互斥量
  • 确保所有线程解锁后再销毁互斥量
  • 加锁与解锁必须成对出现,避免死锁
  • 临界区应尽量简短,避免长时间占用锁
  • **互斥锁无论读写都只能被一个线程持有。****读写锁的核心特点是读锁共享(多个线程可同时持有读锁)、写锁独占(仅一个线程可持有写锁)。**两者都需要初始化,性能取决于场景(读多写少场景读写锁更优)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/12 21:47:59

VIGEMBUS实战:打造自定义游戏控制器方案

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个将自定义硬件输入映射为标准XInput控制器的项目&#xff0c;功能需求&#xff1a;1. 读取USB HID设备原始输入数据 2. 通过VIGEMBUS转换为虚拟Xbox手柄输入 3. 提供可配置…

作者头像 李华
网站建设 2026/3/29 5:23:27

1小时搭建你的私有DNSLOG服务原型

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个可快速部署的DNSLOG服务模板&#xff0c;包含&#xff1a;1) DNS服务器基础配置 2) 请求记录数据库 3) 简易Web界面 4) API端点。用户只需提供域名和服务器IP即可一键部署…

作者头像 李华
网站建设 2026/3/20 19:44:03

深度学习毕设项目:基于卷积神经网络对不同柑橘病变识别

v 博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&…

作者头像 李华
网站建设 2026/3/16 6:46:27

‘麦子交换1‘在农产品交易平台中的实际应用案例

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个农产品交易平台的麦子交换1功能模块。需要实现&#xff1a;1)用户注册登录&#xff0c;2)小麦商品列表展示&#xff0c;3)交易撮合功能&#xff0c;4)订单管理系统。使用R…

作者头像 李华
网站建设 2026/3/26 9:14:15

ABAP---ABAP2XLSX代替ALSM_EXCEL_TO_INTERNAL_TABLE导入 动态创建表结构

问题&#xff1a; ALSM_EXCEL_TO_INTERNAL_TABLE &#xff0c;其底层是通过调用你电脑本地安装的 Excel 进程来读取数据的&#xff0c;因此会高度依赖于前端客户端的本地环境&#xff0c;极不稳定&#xff1b; 解决 使用abap2xlsx的方式导入&#xff1a;直接解析 Excel 的 X…

作者头像 李华