Windows事件等待学习笔记(一)—— 临界区&自旋锁

基础知识

并发:是指多个线程在同时执行

  1. 单核(是分时执行,不是真正的同时)
  2. 多核(在某一个时刻,会同时有多个线程再执行)

同步:则是保证在并发执行的环境中各个线程可以有序的执行

演示代码

注意:如果线程中的代码只对局部变量进行访问,就不存在并发的问题;只有当访问全局变量的时候,才需要关注并发的问题

案例一

DWORD  dwVal = 0;	//全局变量

线程中的代码:

	dwVal++;	//只有一行  安全吗?

答案

dwVal++;	对应的汇编代码:

	mov	eax,[0x12345678]
	add	eax,1				//当第一个线程执行到这里时,产生了线程切换的话
							//第二个线程也执行到这里时,将会出现逻辑错误
	mov	[0x12345678],eax

案例二

当将案例一的三行汇编代码改为一行时:

INC DWORD PTR DS:[0x12345678]		//一行汇编指令,安全吗?

答案:若当前系统为单核时,安全;但如果当前CPU为多核时,不安全(两个核有可能同时执行一行指令)
解决方案:改为LOCK INC DWORD PTR DS:[0x12345678]

LOCK

描述:在多核CPU的情况下,保证只有一个CPU能访问带有LOCK指令内存

可以带LOCK前缀的指令

BT, BTS, BTR, BTC	(mem, reg/imm)
XCHG, XADD			(reg, mem / mem, reg)
ADD, OR, ADC, SBB	(mem, reg/imm)
AND, SUB, XOR		(mem, reg/imm)
NOT, NEG, INC, DEC	(mem)

单行代码原子操作

原子操作相关API

InterlockedIncrement		InterlockedExchangeAdd
InterlockedDecrement		InterlockedFlushSList		
InterlockedExchange			InterlockedPopEntrySList
InterlockedCompareExchange	InterlockedPushEntrySList

InterlockedIncrement 反汇编
在这里插入图片描述

多行代码原子操作

关键代码A	//N行代码要求原子操作
关键代码B	//单独加LOCK可以吗?
关键代码C
.......

答案:不行,试想一下,当第一个线程执行到关键代码B,第二个线程就执行完关键代码A时,此时虽然第二个线程无法执行关键代码B,但却有可能会先执行关键代码C

解决方案临界区

临界区

描述:一次只允许一个线程进入直到离开
实现思路:加锁(全局变量)

演示代码

DWORD dwFlag = 0;	//实现临界区的方式就是加锁
					//锁:全局变量  进去加一 出去减一

if(dwFlag  == 0)	//进入临界区	
{	
	dwFlag   = 1	
	.......
	.......
	.......
		
	dwFlag   = 0	//离开临界区
}

问题:上述演示代码安全吗?
答案:仍不安全,当第一个线程通过if判断并进入if内部,但还未来得及执行dwFlag=1时,第二个线程也有可能通过if判断从而进入if语句内部

手动实现

进入临界区:

Lab:
	mov eax,1
	lock xadd [Flag],eax
	cmp eax,0
	jz endLab
	dec [Flag]
	//线程等待Sleep..
endLab:
	ret

离开临界区

lock dec [Flag]

自旋锁

小提示:单核和多核虽然使用的是同一份内核文件,但里面的代码是有区别的

单核 ntoskrnl.exe 反汇编
在这里插入图片描述
多核 ntoskrnl.exe 反汇编
在这里插入图片描述

分析 KeAcquireSpinLockAtDpcLevel

多核
在这里插入图片描述
单核
在这里插入图片描述

总结

  1. 自旋锁只对多核有意义
    (查看不同版本的KeAcquireSpinLockAtDpcLevel函数)
  2. 自旋锁临界区、事件、互斥体一样,都是一种同步机制,都可以让当前线程处于等待状态,区别在于自旋锁不用切换线程
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
自旋锁的作用是保护临界资源,防止多个线程同时访问该资源导致数据不一致或者其他竞态条件问题。自旋锁和临界资源的绑定是通过自旋锁的使用来实现的。 在访问临界资源之前,需要使用自旋锁来获取定状态。如果自旋锁已经被占用,则代码将一直等待,直到自旋锁的状态变为未定状态才能继续访问临界资源。在访问临界资源完成之后,需要使用自旋锁来释放定状态,以便其他线程能够访问临界资源。 举个例子,假设有一个共享变量count,多个线程需要对其进行读写操作,这时候就可以使用自旋锁来保护count的访问。当某个线程需要访问count时,先使用自旋锁进行加操作,如果自旋锁已经被其他线程占用,则该线程会一直等待,直到自旋锁的状态变为未定状态。当该线程成功获取自旋锁之后,就可以安全地访问count,操作完成后再使用自旋锁进行解操作,以便其他线程也可以访问count。 需要注意的是,自旋锁只适用于单个CPU或者多核处理器上的单个核心,因此在多核处理器上使用自旋锁时需要谨慎评估临界区的访问时间和竞争情况,以避免性能问题。此外,自旋锁一般用于保护临界区访问时间比较短且竞争不激烈的场景,如果临界区访问时间较长或者竞争激烈,建议使用信号量或读写自旋锁等其他同步原语。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值