《Windows核心編程》---Interlocked原子訪問系列函數

轉自:http://blog.csdn.net/ace1985/article/details/5729912

所謂原子訪問,指的是一個線程在訪問某個資源的同時能夠保證沒有其他線程會在同一時刻訪問同一資源。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;

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章