《Windows核心编程》---Interlocked原子访问系列函数
所謂原子訪問,指的是一個線程在訪問某個資源的同時能夠保證沒有其他線程會在同一時刻訪問同一資源。Interlocked系列函數提供了這樣的操作。所有這些函數會以原子方式來操控一個值。
Interlocked函數的工作原理取決于代碼運行的CPU平臺,如果是x86系列CPU,那么Interlocked函數會在總線上維持一個硬件信號,這個信號會阻止其他CPU訪問同一個內存地址。我們必須確保傳給這些函數的變量地址是經過對齊的,否則這些函數可能會失敗。C運行庫提供了一個_aligned_malloc函數,我們可以使用這個函數來分配一塊對齊過的內存:
void?* _aligned_malloc(
????size_t size,??//要分配的字節數
????size_t alignment?//要對齊到的字節邊界,傳給alignment的值必須是2的整數冪次方
);
Interlocked函數的另一個需要注意的點是它們執行得很快。調用一次Interlocked函數通常只占用幾個CPU周期(通常小于50),而且不需要在用戶模式和內核模式之間進行切換(這個切換通常需要占用1000個CPU周期以上)。
?
1)原子加減操作InterlockedExchangeAdd函數原型如下:
LONG?__cdecl?InterlockedExchangeAdd(?//對32位值進行操作
??__inout??LONG?volatile?*Addend,?//需要遞增的變量地址
??__in?????LONG Value?//增量值,可為負值表示減法
);
?
LONGLONG?__cdecl?InterlockedExchangeAdd64(?//對64位值進行操作
??__inout??LONGLONG?volatile?*Addend,
??__in?????LONGLONG Value
);
?
2)InterlockedExchange函數用于原子地將32位整數設為指定的值:
LONG?__cdecl?InterlockedExchange(
??__inout??LONG?volatile?*Target,?//指向要替換的32位值的指針
??__in?????LONG Value?//替換的值
);
返回值是指向原先的32位整數值。
?
InterlockedExchangePointer函數原子地用于替換地址值:
PVOID?__cdecl?InterlockedExchangePointer(
??__inout??PVOID?volatile?*Target,?//指向要替換的地址值的指針
??__in?????PVOID Value?//替換的地址值
);
返回值是原來的地址值。
對32位應用程序來說,以上兩個函數都用一個32位值替換另一個32位值,但對64位應用程序來說,InterlockedExchange替換的是32位值,而InterlockedExchangePointer替換的是64位值。
?
當然,還有一個函數InterlockedExchange64專門用來原子地操作64位值的:
LONGLONG?__cdecl?InterlockedExchange64(
??__inout??LONGLONG?volatile?*Target,
??__in?????LONGLONG Value
);
?
在實現旋轉鎖時,InterlockedExchange函數極其有用:
//標識一個共享資源是否正在被使用的全局變量
BOOL g_fResourceInUse = FALSE;
...
void?ASCEFunc()
{
?????????//等待訪問共享資源
?????????while(InterlockedExchange(&g_fResourceInUse, TRUE) == TRUE)
???????????????????sleep(0);
?????????//訪問共享資源
?????????...
?????????//結束訪問
?????????InterlockedExchange(&g_fResourceInUse, FALSE);
}
注意,在使用這項技術時要小心,因為旋轉鎖會耗費CPU時間。特別是在單CPU機器上應該避免使用旋轉鎖,如果一個線程不停地循環,那么這會浪費寶貴的CPU時間,而且會阻止其他線程改變該鎖的值。
?
3)函數InterlockedCompareExchange函數和InterlockedCompareExchangePointer函數原型如下:
LONG?__cdecl?InterlockedCompareExchange(
??__inout??LONG?volatile?*Destination,?//當前值
??__in?????LONG?Exchange,?//
??__in?????LONG Comparand?//比較值
);
PVOID?__cdecl?InterlockedCompareExchangePointer(
??__inout??PVOID?volatile?*Destination,
??__in?????PVOID?Exchange,
??__in?????PVOID Comparand
);
這兩個函數以原子方式執行一個測試和設置操作。對32位應用程序來說,這兩個函數都對32位值進行操作;在64位應用程序中,InterlockedCompareExchange對32位值進行操作而InterlockedCompareExchangePointer對64位值進行操作。函數會將當前值(Destination指向的)與參數Comparand進行比較,如果兩個值相同,那么函數會將*Destination修改為Exchange參數指定的值。若不等,則*Destination保持不變。函數會返回*Destination原來的值。所有這些操作都是一個原子執行單元來完成的。
當然,這兩個函數的64位版本是:
LONGLONG?__cdecl?InterlockedCompareExchange64(
??__inout??LONGLONG?volatile?*Destination,
??__in?????LONGLONG?Exchange,
??__in?????LONGLONG Comparand
);
?
4)Interlocked單向鏈表函數
InitializeSListHead函數用于創建一個空的單向鏈表棧:
void?WINAPI InitializeSListHead(
??__inout??PSLIST_HEADER ListHead
);
?
InterlockedPushEntrySList函數在棧頂添加一個元素:
PSLIST_ENTRY WINAPI InterlockedPushEntrySList(
??__inout??PSLIST_HEADER ListHead,
??__inout??PSLIST_ENTRY ListEntry
);
?
InterlockedPopEntrySList函數移除位于棧頂的元素并將其返回:
PSLIST_ENTRY WINAPI InterlockedPopEntrySList(
??__inout??PSLIST_HEADER ListHead
);
?
InterlockedFlushSList函數用于清空單向鏈表棧:
PSLIST_ENTRY WINAPI InterlockedFlushSList(
??__inout??PSLIST_HEADER ListHead
);
?
QueryDepthSList函數用于返回棧中元素的數量:
USHORT WINAPI QueryDepthSList(
??__in??PSLIST_HEADER ListHead
);
?
單向鏈表棧中元素的結構是:
typedef?struct?_SLIST_ENTRY {
??struct?_SLIST_ENTRY *Next;
} SLIST_ENTRY, *PSLIST_ENTRY;
注意:所有單向鏈表棧中的元素必須以MEMORY_ALLOCATION_ALIGNMENT方式對齊,使用_aligned_malloc函數即可。
?
實例如下:
#include?<windows.h>
#include?<malloc.h>
#include?<stdio.h>
?
// Structure to be used for a list item; the first member is the
// SLIST_ENTRY structure, and additional members are used for data.
// Here, the data is simply a signature for testing purposes.
?
?
typedef?struct?_PROGRAM_ITEM {
????SLIST_ENTRY ItemEntry;
????ULONG Signature;
} PROGRAM_ITEM, *PPROGRAM_ITEM;
?
int?main( )
{
????ULONG Count;
????PSLIST_ENTRY pFirstEntry, pListEntry;
????PSLIST_HEADER pListHead;
????PPROGRAM_ITEM pProgramItem;
?
????// Initialize the list header to a MEMORY_ALLOCATION_ALIGNMENT boundary.
????pListHead = (PSLIST_HEADER)_aligned_malloc(sizeof(SLIST_HEADER),
???????MEMORY_ALLOCATION_ALIGNMENT);
????if( NULL == pListHead )
????{
????????printf("Memory allocation failed./n");
????????return?-1;
????}
????InitializeSListHead(pListHead);
?
????// Insert 10 items into the list.
????for( Count = 1; Count <= 10; Count += 1 )
????{
????????pProgramItem = (PPROGRAM_ITEM)_aligned_malloc(sizeof(PROGRAM_ITEM),
????????????MEMORY_ALLOCATION_ALIGNMENT);
????????if( NULL == pProgramItem )
????????{
????????????printf("Memory allocation failed./n");
????????????return?-1;
????????}
????????pProgramItem->Signature = Count;
????????pFirstEntry = InterlockedPushEntrySList(pListHead,
???????????????????????&(pProgramItem->ItemEntry));
????}
?
????// Remove 10 items from the list and display the signature.
????for( Count = 10; Count >= 1; Count -= 1 )
????{
????????pListEntry = InterlockedPopEntrySList(pListHead);
?
????????if( NULL == pListEntry )
????????{
????????????printf("List is empty./n");
????????????return?-1;
????????}
?
????????pProgramItem = (PPROGRAM_ITEM)pListEntry;
????????printf("Signature is %d/n", pProgramItem->Signature);
?
????// This example assumes that the SLIST_ENTRY structure is the
????// first member of the structure. If your structure does not
????// follow this convention, you must compute the starting address
????// of the structure before calling the free function.
?
????????_aligned_free(pListEntry);
????}
?
????// Flush the list and verify that the items are gone.
????pListEntry = InterlockedFlushSList(pListHead);
????pFirstEntry = InterlockedPopEntrySList(pListHead);
????if?(pFirstEntry != NULL)
????{
????????printf("Error: List is not empty./n");
????????return?-1;
????}
?
????_aligned_free(pListHead);
?
????return?1;
}
總結
以上是生活随笔為你收集整理的《Windows核心编程》---Interlocked原子访问系列函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: \sdk\include\wspiapi
- 下一篇: 将可执行文件以资源的方式加入到另一个可执