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

【多线程】互斥锁(Mutex)是什么?

【多线程】互斥锁(Mutex)是什么?

本文来自于我关于多线程系列文章。欢迎阅读、点评与交流
1.【多线程】互斥锁(Mutex)是什么?
2.【多线程】信号量(Semaphore)是什么?
3.【多线程】信号量(Semaphore)常见的应用场景

互斥锁(Mutex) 是多线程编程中最基本、最重要的同步工具之一。它的名字揭示了它的核心功能:互斥(Mutual Exclusion)

一句话概括

互斥锁就像是一个只有一个钥匙的厕所。一个人(线程)进去后,会把门锁上(加锁),手里拿着唯一的钥匙。其他想用厕所的人(线程)必须等在门口(阻塞),直到里面的人出来(解锁)并把钥匙交给下一个人才行。


核心概念

互斥锁用于保护临界区(Critical Section)。临界区是一段代码,在这段代码中会访问共享资源(如全局变量、文件、设备等),这段代码不能被多个线程同时执行,否则可能导致数据不一致、逻辑错误等问题。

互斥锁提供了两个基本操作:

  1. 加锁(Lock)

    • 一个线程在进入临界区之前,必须尝试获取锁。
    • 如果锁当前是空闲的(未被任何线程持有),那么该线程成功获得锁,并进入临界区执行。
    • 如果锁已经被其他线程持有,那么当前线程会被阻塞(即进入等待状态),直到锁被释放。
  2. 解锁(Unlock)

    • 一个线程在离开临界区时,必须释放它持有的锁。
    • 这个操作会使锁变为空闲状态。如果有其他线程正在等待这个锁,操作系统会唤醒其中一个等待的线程,该被唤醒的线程随后可以获得锁并继续执行。

一个生动的比喻:会议室与白板

想象一个团队共用一间会议室和一块白板(共享资源)进行头脑风暴。

  • 互斥锁就像是会议室门上的一个状态牌,只有两种状态:“空闲”或“使用中”。
  • 线程就像是想要使用会议室的团队成员。
  • 临界区就是使用会议室和白板进行讨论的整个过程。

工作流程如下:

  1. Alice想用会议室。她看到状态是“空闲”,于是把它翻到“使用中”,然后进去开始在白板上写写画画。(加锁成功
  2. Bob也想用会议室。他来到门口,看到状态是“使用中”,于是他只好在门口等待。(尝试加锁,但被阻塞
  3. Alice讨论完了,擦干净白板,走出会议室,并把状态牌翻回“空闲”。(解锁
  4. Bob看到状态变为“空闲”,立即将牌子翻到“使用中”,然后进入会议室使用。(被唤醒并加锁成功

这个机制确保了同一时间只有一个人能在白板上写字,避免了 ideas 被混乱地写在一起。


关键特性

  1. 所有权(Ownership)

    • 这是互斥锁与信号量最关键的区别。互斥锁有所有者的概念,即哪个线程加了锁,就必须由同一个线程来解锁。其他线程不能帮你解锁。
    • 这个特性避免了许多复杂的同步错误。
  2. 原子性(Atomic Operations)

    • 检查锁状态和获取锁这两个动作是合并为一个原子操作完成的。这意味着在这个操作过程中不会被其他线程打断,从而保证了即使多个线程同时尝试加锁,也只有一个能成功。
  3. 排队(Queuing)

    • 当多个线程等待同一个锁时,它们通常会进入一个等待队列。当锁被释放时,操作系统会从队列中选一个线程(具体的挑选策略可能因系统而异,如先进先出FIFO或优先级)来唤醒它。

代码示例(伪代码)

让我们看一个经典的“银行账户取款”例子。如果没有锁,多个线程同时取款会导致余额错误。

// 伪代码// 1. 定义一个全局的互斥锁
Mutex lock;
// 2. 共享资源:账户余额
int account_balance = 1000;void withdraw_money(int amount) {// 3. 进入临界区前:加锁lock.lock(); // --- 临界区开始 ---// 以下操作是安全的,不会被其他线程打断if (account_balance >= amount) {// 模拟一些操作时间,如果没有锁,问题更容易在这里暴露sleep(1); account_balance = account_balance - amount;print("取款成功,余额为:", account_balance);} else {print("余额不足!");}// --- 临界区结束 ---// 4. 离开临界区:解锁lock.unlock(); 
}// 主程序
main() {Thread thread1 = create_thread(withdraw_money, 800); // 线程1取800Thread thread2 = create_thread(withdraw_money, 800); // 线程2也取800thread1.join();thread2.join();
}

可能的结果:

  • 无锁情况:两个线程都检查余额(1000>800),都认为可以取款,最终余额可能变为 -600,这是严重的错误。
  • 有锁情况
    • 线程1先获得锁,执行完整个取款流程(余额变为200)后释放锁。
    • 线程2然后获得锁,检查余额(200 < 800),打印“余额不足”。
    • 结果是正确的。

互斥锁(Mutex) vs. 信号量(Semaphore)

为了更清晰地理解,我们再对比一下:

特性互斥锁 (Mutex)二进制信号量 (Binary Semaphore, =1)
核心目的实现互斥发信号、同步(也可用于互斥)
所有权。锁的持有者必须负责解锁。。任何线程都可以执行 V()(释放)。
行为保护一个临界区,让线程“串行”访问。像一个开关,通知一个线程某个事件已发生。
复杂度/风险简单直观,不易出错。更灵活,但也更容易误用(如错误的释放顺序)。

简单记:互斥锁是“谁上锁,谁解锁”,用于保护资源;信号量是“谁都可以发信号”,用于协调工作。

总结

  • 互斥锁是什么? 一个提供互斥功能的同步原语。
  • 为什么需要它? 为了保护共享资源,防止多个线程同时访问造成竞争条件(Race Condition)
  • 怎么工作? 通过 lock()unlock() 两个操作,确保一次只有一个线程能进入被保护的临界区
  • 关键特点? 所有权概念,即加锁和解锁必须是同一个线程。

它是一种“防御性”的编程机制,是构建线程安全程序的基础。

http://www.dtcms.com/a/403145.html

相关文章:

  • 18.1 Python+AI一键生成PPT!ChatPPT核心技术深度解析
  • 影响网站权重的因素有哪些wordpress 仪表盘 渗透
  • Nginx反向代理与缓存功能-第一章
  • 精读《C++20设计模式》——创造型设计模式:构建器系列
  • SpringCloud高可用集群搭建及负载均衡配置实战
  • AI产品独立开发完全指南:技术栈选择、案例分析与商业化路径
  • Jenkins+Tomcat持续集成教程
  • 哪里有免费建设网站承德在线
  • 【金融保研复习】知识点与可能的题目
  • 基于ZYNQ的ARM+FPGA+yolo AI火灾实时监测与识别系统
  • 【Python语法基础学习笔记】常用函数
  • Uniapp运行时错误修复报告
  • PHP 8.0+ 高级特性深度探索:架构设计与性能优化
  • 网站管理建设总结大数据营销的概念
  • 顺德品牌网站建设辽宁建设工程信息网上
  • Oracle Clint11g安装
  • Gerkin+unittest(python)实现自动化
  • MySQL基础语法大全
  • 从企业实战中学习Appium自动化(二)
  • Unity 使用ADB工具打包Apk 安装到Android手机或平板
  • 一、移动零,复写零,快乐数
  • React资源合集
  • sem是什么职业邢台做网站建设优化制作公司
  • 福建省建设执业资格注册中心网站企业建设网站注意点
  • 配置Modbus TCP转RS485模块读取温度数据
  • OSPF LSA/ 路由种类
  • 算法面试(5)------NMS(非极大值抑制)原理 Soft-NMS、DIoU-NMS 是什么?
  • 乐清网站制作推荐建湖人才网最新招聘信息
  • AWS下载sentinel-2原始影像
  • docker-容器网络类型