Mutex 互斥锁

版权声明:本文为博主原创文章,未经博主允许不得转载。
https://blog.youkuaiyun.com/huangweiqing80/article/details/83060119

1、理解互斥锁?

互斥锁的使用也是保持内核临界区的同步的,互斥锁可以说源于信号量,信号量设置计数器可以容许n个进程并发的访问临界区,而互斥锁不行,只能容许每次一个进程访问,也就是计数器值为1的信号量,可以这么理解。互斥锁和自旋锁有不同(显然的),互斥锁在中断处理程序中和可延迟函数中都不能使用,因为它是可以睡眠的,只能在进程上下文或者软中断上下文才能使用。

struct mutex {
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
atomic_t count;
spinlock_t wait_lock;
struct list_head wait_list;
};

再解释下struct mutex成员的含义:
count: count是一个原子变量,(关于原子变量不懂的,可以看前面的原子变量文章)。 当count=1代表资源可用,等于0代表资源不可用,加锁状态。 负值代表有等待者。
wait_lock: 是一个自旋锁变量, 用于对wait_list的操作变为原子变量
wait_list : 用于管理那些在获取mutex的进程,在无法获取互斥锁的时候,则把线程放入wait_List队列,让线程进入睡眠。wait_list是一个等待队列的链表头,这个链表将所有等待该互斥锁的进程组成一个链表结构。在这个链表中,存放了正在睡眠的进程链表。内核定义了互斥锁的等待队列结构体:

struct mutex_waiter {
	struct list_head	list;
	struct task_struct	*task;
#ifdef CONFIG_DEBUG_MUTEXES
	void			*magic;
#ifdef CONFIG_MT_DEBUG_MUTEXES
	struct task_struct	*task_wait_on;
#endif
#endif
};

Mutex(互斥锁

<think>我们正在讨论在EasyX图形库中使用互斥锁mutex)的作用及其在多线程环境下的应用。根据之前的讨论,我们知道EasyX图形库在多线程环境下可能会遇到资源竞争问题,导致绘图混乱或程序崩溃。互斥锁正是用来解决这类问题的同步机制。###互斥锁在EasyX多线程中的作用互斥锁mutex)是一种用于多线程编程中防止多个线程同时访问共享资源的同步原语。在EasyX图形库中,互斥锁的主要作用是:1.**避免绘图冲突**:当多个线程同时调用EasyX绘图函数(如`fillcircle`,`line`,`cleardevice`等)时,可能会发生资源竞争,导致绘图混乱。互斥锁可以确保在同一时间只有一个线程执行绘图操作,从而保证绘图的正确性。2.**保护共享数据**:在多线程程序中,除了绘图函数外,共享数据(如全局变量`ballX`)也可能被多个线程同时读写。互斥锁可以保护这些数据,防止出现数据竞争(datarace),确保数据的一致性。###互斥锁的应用方法在C++中,我们使用标准库中的`std::mutex`来创建互斥锁。具体应用步骤如下:1.**定义互斥锁**:在全局或适当的作用域中定义一个互斥锁对象。```cpp#include<mutex>std::mutexmtx;//全局互斥锁```2.**在关键区域加锁**:在需要保护的代码段(称为临界区)前调用`lock()`方法,在代码段后调用`unlock()`方法。也可以使用`std::lock_guard`自动管理锁,避免忘记解锁。```cpp//使用lock()和unlock()mtx.lock();//执行绘图操作或修改共享数据mtx.unlock();//或使用lock_guard(推荐,更安全){std::lock_guard<std::mutex>lock(mtx);//执行绘图操作或修改共享数据}//离开作用域时自动解锁```###在EasyX多线程程序中使用互斥锁的示例以下是一个改进的示例,展示互斥锁如何保护EasyX的绘图操作和共享数据:```cpp#include<graphics.h>#include<thread>#include<mutex>#include<iostream>std::mutexdrawMutex;//互斥锁,用于保护绘图操作和共享数据intballX=100;//共享数据:小球x坐标//绘制小球的线程函数voiddrawBall(){while(!kbhit()){//当没有按键时循环{std::lock_guard<std::mutex>lock(drawMutex);cleardevice();//清屏setfillcolor(GREEN);fillcircle(ballX,300,30);//绘制小球}//自动解锁Sleep(50);//短暂休眠,让出CPU}}//移动小球的线程函数voidmoveBall(){while(!kbhit()){{std::lock_guard<std::mutex>lock(drawMutex);ballX=(ballX+5)%600;//修改小球位置(共享数据)}Sleep(100);//控制移动速度}}intmain(){initgraph(800,600);//初始化图形窗口//创建两个线程:一个绘制,一个移动std::threadt1(drawBall);std::threadt2(moveBall);t1.join();t2.join();closegraph();return0;}```**代码说明**:-两个线程`t1`(绘制小球)和`t2`(移动小球)都需要访问共享资源:`ballX`和图形设备(通过绘图函数)。-在修改`ballX`和调用绘图函数之前,都使用`std::lock_guard`加锁,确保这些操作是原子的(即不会被其他线程打断)。-使用`kbhit()`检测按键,以便按任意键退出循环。###注意事项1.**锁的粒度**:锁的粒度不宜过大,否则会降低并发性能。例如,在绘图线程中,我们只锁定了清屏和绘制小球的操作,而没有锁定整个循环。同时,在移动线程中,只锁定了修改`ballX`的语句。2.**避免死锁**:死锁发生在两个或多个线程互相等待对方释放锁时。在EasyX多线程编程中,死锁通常发生在嵌套锁或多个互斥锁使用不当时。因此,应尽量避免同时使用多个互斥锁,或确保它们以相同的顺序上锁。3.**使用RAII管理锁**:推荐使用`std::lock_guard`或`std::unique_lock`,它们在构造时加锁,在析构时自动解锁,即使发生异常也能保证解锁,从而避免死锁。###总结在EasyX多线程编程中,互斥锁的作用是确保绘图操作和共享数据的访问是线程安全的,从而避免资源竞争导致的绘图混乱或程序崩溃。通过合理使用互斥锁,可以构建稳定、高效的多线程图形应用程序。§§相关问题§§1.除了互斥锁,还有哪些同步机制可用于EasyX多线程编程?2.如何在EasyX多线程中处理用户输入事件?3.互斥锁的使用会对程序性能产生什么影响?如何优化?</think>### EasyX 多线程中互斥锁Mutex)的作用与应用 在 C++ EasyX 多线程编程中,互斥锁(`std::mutex`)是解决**资源竞争问题**的核心同步机制,其作用主要体现在以下方面: #### 一、互斥锁的核心作用 1. **防止绘图冲突** EasyX 的绘图函数(如 `fillcircle`, `line`, `cleardevice`)**非线程安全**。当多个线程同时调用这些函数时,会导致: - 图形错位或闪烁 - 颜色设置被意外覆盖 - 清屏操作中断绘图过程 互斥锁确保同一时间只有一个线程访问图形资源[^3]。 2. **保护共享数据** 在多线程动画中(如小球坐标 `ballX`),互斥锁防止数据竞争: ```cpp std::mutex mtx; int ballX = 0; // 共享变量 void updatePosition() { mtx.lock(); // 加锁 ballX += 5; // 安全修改 mtx.unlock(); // 解锁 } ``` 避免一个线程读取时另一个线程正在写入,导致数据不一致。 3. **保证操作原子性** 将相关绘图操作捆绑为不可分割的单元: ```cpp void drawScene() { std::lock_guard<std::mutex> lock(mtx); // RAII自动锁 setfillcolor(RED); // 颜色设置 fillcircle(x, y, 30); // 绘图 // 解锁自动发生 } ``` #### 二、互斥锁的实际应用 ##### 场景示例:双线程动画 ```cpp #include <graphics.h> #include <thread> #include <mutex> std::mutex drawMutex; int posX = 100, posY = 100; // 线程1:移动物体 void moveObject() { while (true) { { std::lock_guard<std::mutex> lock(drawMutex); posX = (posX + 5) % 600; // 修改共享位置 } Sleep(50); } } // 线程2:绘制物体 void drawObject() { while (true) { { std::lock_guard<std::mutex> lock(drawMutex); cleardevice(); fillcircle(posX, posY, 30); // 安全绘图 } Sleep(30); } } int main() { initgraph(800, 600); std::thread t1(moveObject); std::thread t2(drawObject); t1.join(); t2.join(); closegraph(); } ``` **关键点**: 1. 使用 `std::lock_guard` 实现 RAII(资源获取即初始化),避免忘记解锁 2. 通过 `{}` 控制锁的作用域,最小化锁定时间 3. 两个线程通过互斥锁安全协作: - 移动线程修改位置 - 绘制线程读取位置并渲染 #### 三、高级应用技巧 1. **多锁管理** 当存在多个独立资源时,使用专用锁减少竞争: ```cpp std::mutex posLock; // 位置数据锁 std::mutex colorLock; // 颜色数据锁 ``` 2. **条件变量配合** 实现线程间通知机制(如暂停/继续动画): ```cpp std::condition_variable cv; bool paused = false; void renderThread() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{ return !paused; }); // 等待唤醒 // 执行绘图... } ``` 3. **性能优化** - 优先锁定数据而非整个函数 - 避免在锁内调用耗时操作(如文件 I/O) - 使用 `try_lock()` 避免死锁: ```cpp if (mtx.try_lock()) { // 快速操作 mtx.unlock(); } ``` #### 四、常见错误及规避 | 问题类型 | 错误示例 | 解决方案 | |---------|---------|---------| | **死锁** | 嵌套锁定未按顺序 | 使用 `std::lock()` 同时锁定多个互斥量 | | **数据竞争** | 未保护共享变量 | 对所有共享数据加锁 | | **锁粒度不当** | 锁住整个渲染循环 | 仅锁定关键操作段 | | **忘记解锁** | `lock()` 后无 `unlock()` | 使用 `lock_guard` 自动管理 | > **关键结论**:在 EasyX 多线程中,互斥锁是**协调图形操作和数据访问**的基础设施。合理使用可解决 90% 的资源竞争问题,确保动画流畅且数据一致[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值