news 2026/5/1 8:01:56

线程同步之互斥量

作者头像

张小明

前端开发工程师

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/4/30 16:12:14

CentOS7性能调优:让你的服务器快如闪电

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 编写一个CentOS7性能优化脚本&#xff0c;自动执行以下优化操作&#xff1a;1. 调整内核参数&#xff08;sysctl.conf&#xff09;&#xff1b;2. 优化文件系统挂载选项&#xff1…

作者头像 李华
网站建设 2026/4/28 6:29:51

如何激发团队的集体好奇心

如何激发团队的集体好奇心 关键词:团队管理、集体好奇心、创新思维、团队激励、知识共享 摘要:本文聚焦于如何激发团队的集体好奇心这一重要课题。首先介绍了研究此问题的背景和目的,明确预期读者。接着阐述了集体好奇心的核心概念及其与团队发展的联系。详细讲解了激发集体…

作者头像 李华
网站建设 2026/5/1 8:01:02

5个场景告诉你为什么需要专业解压软件

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个场景化解压工具&#xff0c;针对不同使用场景提供定制解压方案&#xff1a;1)办公场景-批量解压邮件附件 2)开发场景-自动解压并部署代码包 3)游戏场景-快速解压大型游戏资…

作者头像 李华
网站建设 2026/4/23 13:19:20

零基础入门:用ENSP PRO完成第一个网络实验

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 为网络新手设计一个入门实验&#xff1a;1) 创建包含2台PC和1台交换机的简单拓扑&#xff1b;2) 配置IP地址实现互通&#xff1b;3) 抓包分析ARP和Ping过程&#xff1b;4) 添加路由…

作者头像 李华
网站建设 2026/4/30 3:20:58

如何用AI加速密码破解工具开发

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个基于AI的密码破解工具&#xff0c;支持多种加密算法&#xff08;如MD5、SHA-1等&#xff09;。工具应具备智能字典生成功能&#xff0c;能根据目标系统特征自动优化破解策…

作者头像 李华
网站建设 2026/5/1 6:56:43

用NAVICAT社区版快速构建数据库原型

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 设计一个数据库原型开发工具包&#xff0c;基于NAVICAT社区版实现&#xff1a;1. 模板库&#xff08;用户管理、电商、博客等常见模式&#xff09;&#xff1b;2. 快速生成DDL语句…

作者头像 李华