原子操作的Interlocked函數
// 自增操作 int32 n1 = 100; // n1=n1+1; r1=n1; int32 r1 = FPlatformAtomics::InterlockedIncrement(&n1); // n1=101 r1=101 // 自減操作 int32 n2 = 110; // n2=n2-1; r2=n2; int32 r2 = FPlatformAtomics::InterlockedDecrement(&n2); // n2=109 r2=109 // Fetch and Add操作 int32 n3 = 120; // r3=n3; n3=n3+5; int32 r3 = FPlatformAtomics::InterlockedAdd(&n3, 5); // r3=120 n3=125 // 數值Swap操作 int32 n4 = 130; // r4=n4; n4=8; int32 r4 = FPlatformAtomics::InterlockedExchange(&n4, 8); // r4=130 n4=8 // 指針Swap操作 int32 n51 = 140; int32 n52 = 141; int32* pn51 = &n51; int32* pn52 = &n52; // r5=pn51; pn51=pn52; void* r5 = FPlatformAtomics::InterlockedExchangePtr((void**)&pn51, pn52); // *r5=140 *pn51=141 *pn52=141 // 數值Compare and Swap操作 int32 n61 = 150; int32 n62 = 151; int32 n63 = 150; // r6=n61; if (n61==n63) {n61=n62;} int32 r6 = FPlatformAtomics::InterlockedCompareExchange(&n61, n62, n63); // r6=150 n61=151 n62=151 int32 n71 = 160; int32 n72 = 161; int32 n73 = 60; // r7=n71; if (n71==n73) {n71=n72;} int32 r7 = FPlatformAtomics::InterlockedCompareExchange(&n71, n72, n73); // r7=160 n71=160 n72=161 // 指針Compare and Swap操作 int32 n611 = 150; int32 n622 = 151; int32 n633 = 150; int32* pn611 = &n611; int32* pn622 = &n622; int32* pn633 = &n633; // r6x=pn611; if (pn611==pn633) {pn611=pn622;} 注:比較的是指針地址 void* r6x = FPlatformAtomics::InterlockedCompareExchangePointer((void**)&pn611, pn622, pn633); // r6x=pn611 *pn611=150 *pn622=151 int32 n711 = 160; int32 n722 = 161; int32* pn711 = &n711; int32* pn722 = &n722; int32* pn733 = &n711; // r7x=pn711; if (pn711==pn733) {pn711=pn722;} 注:比較的是指針地址 void* r7x = FPlatformAtomics::InterlockedCompareExchangePointer((void**)&pn711, pn722, pn733); // r7x=pn711 pn711=pn722 *pn711=161 *pn722=161 // 數值與運算 int32 n81 = 8; int32 n82 = 12; int32 r8 = FPlatformAtomics::InterlockedAnd(&n81, n82); //r8=8 n81=1000&1100=1000=8 n82=12 // 數值或運算 int32 n91 = 9; int32 n92 = 12; int32 r9 = FPlatformAtomics::InterlockedOr(&n91, n92); //r9=9 n91=1001|1100=1101=13 n92=12 // 數值異或運算 int32 na1 = 9; int32 na2 = 12; int32 ra = FPlatformAtomics::InterlockedXor(&na1, na2); // ra=9 na1=1001^1100=0101=5 na2=12
FCriticalSection(用戶模式下的臨界區段)
當有線程進入臨界區段時,其他線程必須等待。基於原子操作Interlocked函數實現。
優點:效率高(不需要昂貴的用戶態切換到內核態的上下文切換)
缺點:不能用於進程間同步,只能用於進程內各線程間同步
平台 | 實現類 |
Windows | typedef FWindowsCriticalSection FCriticalSection; |
Mac Unix Android IOS |
typedef FPThreadsCriticalSection FCriticalSection; |
HoloLens | typedef FHoloLensCriticalSection FCriticalSection; |
FSystemWideCriticalSection(系統范圍的臨界區段)
當有線程進入臨界區段時,其他線程必須等待。基於內核對象Mutex(互斥體)實現。
優點:可用於系統范圍內進程間同步,也可以用於進程內各線程間同步
缺點:效率低(有昂貴的用戶態切換到內核態的上下文切換)
平台 | 實現類 |
Windows | typedef FWindowsSystemWideCriticalSection FSystemWideCriticalSection; |
Mac | typedef FMacSystemWideCriticalSection FSystemWideCriticalSection; |
Unix | typedef FUnixSystemWideCriticalSection FSystemWideCriticalSection; |
Android IOS HoloLens |
// 未實現 typedef FSystemWideCriticalSectionNotImplemented FSystemWideCriticalSection; |
FRWLock(讀寫鎖)
由於讀線程並不會破壞數據,因此讀寫鎖將對鎖操作的線程分成:讀線程和寫線程。以提高並發效率。
讀線程以共享模式(share)來獲取和釋放鎖,寫線程以獨占模式(exclusive)來獲取和釋放鎖。
當沒有寫線程時,各個讀線程可並發運行。寫線程與寫線程是互斥的;寫線程與讀線程也是互斥的。
平台 | 實現類 |
Windows | typedef FWindowsRWLock FRWLock; |
Mac Unix Android IOS |
typedef FPThreadsRWLock FRWLock; |
HoloLens | typedef FHoloLensRWLock FRWLock; |
FEvent(事件對象)
基於操作系統內核對象實現。ue4中通過FEvent* FPlatformProcess::CreateSynchEvent(bool bIsManualReset)來創建;或者調用FEvent* GetSynchEventFromPool(bool bIsManualReset)從緩存池里面獲取一個。
注:bIsManualReset為TRUE表示為手動重置事件;為FALSE表示為自動重置事件。操作系統將所有等待該Event線程切換到就緒后,會自動調用Reset將Event設置成未觸發狀態。因此,開發者自己不需要顯示地調用Reset。
執行Trigger函數,可將Event設置成觸發狀態;執行Reset函數,可將Event設置成未觸發狀態。
調用Wait函數來等待一個Event。注:Event處於未觸發狀態時,會阻塞等待。
平台 | 實現類 |
Windows | FEventWin : public FEvent |
Mac Unix Android IOS |
FPThreadEvent : public FEvent |
HoloLens | FEventHoloLens : public FEvent |
FSemaphore(信號量)
僅在windows平台上實現,詳見:FWindowsSemaphore
建議使用FEvent來替代
線程安全(Threadsafe)的容器
包括TArray,、TMap、TSet在內的容器都不是線程安全的,需要自己對同步進行管理。
類型 | 解釋說明 |
TArrayWithThreadsafeAdd | 從TArray上派生 提供了AddThreadsafe函數來線程安全地往數組中添加元素
注1:不會引發擴容時,AddThreadsafe才線程安全 注2:其他的操作(Add、Remove、Empty等)都不是線程安全的
|
TLockFreePointerListFIFO | 無鎖隊列(lock free queue),先進先出(FIFO)
基類FLockFreePointerFIFOBase template<class T, int TPaddingForCacheContention, uint64 TABAInc = 1> // TABAInc為解決無鎖編程中ABA問題添加的參數值 |
TLockFreePointerListUnordered | 無鎖棧(lock free stack),后進先出(LIFO)
基類FLockFreePointerListLIFOBase template<class T, int TPaddingForCacheContention, uint64 TABAInc = 1> // TABAInc為解決無鎖編程中ABA問題添加的參數值 |
TLockFreePointerListLIFO | 內存空隙為0的無鎖棧,后進先出(LIFO) 等價於TLockFreePointerListUnordered<T,TPaddingForCacheContention=0> |
FStallingTaskQueue | 無鎖任務隊列
線程在while循環里不斷的從隊列里取任務然后執行,如果隊列是空的,就會空跑while循環。雖然循環沒有多少邏輯,也是會耗費系統資源的 比較好的辦法就是讓線程在沒有任務執行時Wait等待,當把任務加進隊列之后再Trigger喚醒線程,繼續while循環 FStallingTaskQueue就是用來作此優化的類,stalling就是擱置線程的意思。另外它包含兩個FLockFreePointerFIFOBase無鎖隊列,作為高優先級和低優先級隊列 雖然真正讓線程Wait等待和Trigger喚醒的地方並不是在這個類里實現的,但它維護了各個線程的擱置狀態 這些狀態記錄在一個TDoublePtr MasterState里,還是這個64位指針,不過這次它的低26位不是指針了,而是表示26個線程的擱置狀態,1表示被擱置,可以被喚醒 高於26位的部分仍然作為標記計數,防止ABA問題 |
TQueue | 基於鏈表(linked list)實現的不能插隊(non-intrusive)的無鎖隊列(lock free queue),先進先出(FIFO) 有兩種模式(EQueueMode):Mpsc(多生產者單消費者)和Spsc(單生產者單消費者)。 其中Spsc模式是無競爭(contention free)的。 從Head處入隊(Enqueue),從Tail處出隊(Dequeue)。示意圖如下: Tail Head | | V V | Node C | --> | Node B | --> | Node A | --> | nullptr |
|
TCircularQueue | 基於數組(TArray)實現的循環無鎖隊列,先進先出(FIFO) 在僅有一個生產者和一個消費者時,線程安全 |
線程安全(Threadsafe)的Helper工具類
類型 | 解釋說明 |
FThreadSafeCounter | 基於原子操作的FPlatformAtomics::Interlocked函數實現的線程安全的int32計數器 如:FThreadSafeCounter OutstandingHeartbeats; |
FThreadSafeCounter64 | 基於原子操作的FPlatformAtomics::Interlocked函數實現的線程安全的int64計數器 |
FThreadSafeBool | 從FThreadSafeCounter上派生實現的線程安全的Bool |
TThreadSingleton | 為每一個線程創建單例 |
FThreadIdleStats | 從TThreadSingleton派生,用於計算各個線程的等待時間的統計邏輯 |
FMemStack | 從TThreadSingleton派生,基於每個線程進行內存分配 |
TLockFreeFixedSizeAllocator | 固定大小的lockfree的內存分配器 |
TLockFreeClassAllocator | 從TLockFreeFixedSizeAllocator派生的對象內存分配器 |
FScopeLock | 基於作用域的自旋鎖 利用c++的RAII(Resource Acquisition is Initialization)特性(即:對象構造的時候其所需的資源便應該在構造函數中初始化,而對象析構的時候則釋放這些資源),基於FCriticalSection實現的自旋鎖(或旋轉鎖)。 { |
FScopedEvent | 基於作用域的同步事件 利用c++的RAII(Resource Acquisition is Initialization)特性,基於FEvent實現的同步等待事件。 { |
參考
Concurrency & Parallelism in UE4