线程同步的方式

date
Sep 7, 2023
slug
thread-sync-methods
status
Published
tags
Windows Internals
Programming
summary
讨论了在Windows内核编程中如何进行多线程同步。它介绍了一些常用的同步原语,包括互锁操作、分发器对象、互斥量、快速互斥量、信号量、事件、执行体资源、自旋锁等。这些原语用于确保多线程之间的协调工作,以避免竞争条件和死锁。需要注意的是,不同的同步原语适用于不同的情况,选择正确的原语对于编写高效的多线程代码非常重要。
type
Post

背景

多线程在现代计算机上已经是随处可见了,保证多线程正常、高效的协调工作就显得尤为重要了。
内核也考虑到这个问题了,所以提供了一些原语来帮助达成适当的同步。

对比

原语或技术
说明
相关方法
备注
互锁操作
一种CPU的特殊指令(CPU内联函数)
InterlockedIncrement/InterlockedDecrement/InterlockedAdd/InterlockedExchange/InterlockedCompareExchange
常用于无锁编程中,可以在不使用软件对象的前提下执行复杂的原子操作。
分发器对象(可等待对象)
此类型对象将分为有信号和无信号。它们被称为“可等待的”是因为线程 可以在这样的对象上待直至它们变成有信号。在等待期间线程不会消耗CPU周期,因为它处于等待状态。
KeWaitForSingleObject/KeWaitForMultipleObjects
从等待函数返回的所有值传递给NT_SUCCESS宏都返回真。
互斥量(mutant)
互斥量是一种经典的对象,用于解决多个线程中的某个线程在任何时刻访问共享资源的标准问题。
KeInitializeMutex/KeReleaseMutex
互斥量在使用前必须初始化;使用完后必须释放互斥量。可以通过 RAII 来优雅实现。
快速互斥量
快速互斥量是传统互斥量的一个替代,提供了更好的性能。
ExInitializeFastMutex/ExAcquireFastMutex/ExAcquireFastMutexUnsafe(IRQL 为 APC_LEVEL)/ExReleaseFastMutex/ExReleaseFastMutexUnsafe
快速互斥量不能递归地获取。这么做会造成死锁;快速互斥量被获取之后,CPU IRQL会提升到APC_LEVEL(1)。这会阻止该线程上APC的传递;快速互斥量只能无限等待—无法指定超时值。
信号量
信号量的主要目标是用来限制某些东西,比如队列的长度。
KeInitializeSemaphore/KeReleaseSemaphore
一个最大值为一的信号量是否相当于一个互斥量?首先,这看上去 是正确的,但其实不然。信号量没有所有权,一个线程获取的信号量可以被另一个线程释放。
事件
事件封装了一个布尔型的标志—要么真(有信号)要么假(无信号)。事件的主要目的是在某事发生时发出信号,提供执行流上的同 步。
KeInitializeEvent/KeResetEvent/KeClearEvent
事件有两种类型:通知事件(手动重置)和同步事件(自动重置)。
执行体资源
适合单写多读场景的一种同步原语,是另一种特殊对象,并不属于分发器对象。
ExInitializeResourceLite/ExAcquireResourceExclusiveLite/ExAcquireResourceSharedLite/ExReleaseResourceList
调用获取和释放函数的先决条件是必须禁止通常的内核APC。可以调用KeEnterCriticalRegion/KeLeaveCriticalRegion来做到。
自旋锁(针对高IRQL)
自旋锁是内存中的一个简单位,通过API提供原子化的测试和修改操作。当CPU想要获取自旋锁而它当前并不自由的话,CPU会一直在自旋锁上自旋,等待它被另一个CPU释放。
KeAcquireSpinLock/KeReleaseSpinLock/KeAcquireSpinLockAtDpcLevel/KeReleaseSpinLockFromDpcLevel/KeAcquireInterruptSpinLock/KeReleaseInterruptSpinLock
如果获取了自旋锁,千万记得在同一个函数中释放它,否则就有死 锁的风险。类似的还有 排队自旋锁。
 

© Frend Guo 2022 - 2024