1. 程式人生 > >多線程編程之原子操作

多線程編程之原子操作

簡單 bsp idt 進行 gin 復制代碼 effect lean cati

在多線程環境中,對共享的變量的訪問,可以使用基於Compare And Swap這種lock free的技術進行實現,這種實現的好處是效率高。

一、原子操作摘錄

1.1 Android

  源碼:system/core/libcutils /atomic.c(針對X86):

技術分享圖片 技術分享圖片
 1 #elif defined(__i386__) || defined(__x86_64__)
 2 
 3 void android_atomic_write(int32_t value, volatile int32_t* addr) {
 4     int32_t oldValue;
 5     do {
 6         oldValue = *addr;
 7     } while (android_atomic_cmpxchg(oldValue, value, addr));
 8 }
 9 
10 int32_t android_atomic_inc(volatile int32_t* addr) {
11     int32_t oldValue;
12     do {
13         oldValue = *addr;
14     } while (android_atomic_cmpxchg(oldValue, oldValue+1, addr));
15     return oldValue;
16 }
17 
18 int32_t android_atomic_dec(volatile int32_t* addr) {
19     int32_t oldValue;
20     do {
21         oldValue = *addr;
22     } while (android_atomic_cmpxchg(oldValue, oldValue-1, addr));
23     return oldValue;
24 }
25 
26 int32_t android_atomic_add(int32_t value, volatile int32_t* addr){
27     int32_t oldValue;
28     do {
29         oldValue = *addr;
30     } while (android_atomic_cmpxchg(oldValue, oldValue+value, addr));
31     return oldValue;
32 }
33 
34 int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue, volatile int32_t* addr) {
35     int xchg;
36     asm volatile
37     (
38         " lock; cmpxchg %%ecx, (%%edx);"
39         " setne %%al;"
40         " andl $1, %%eax"
41         : "=a" (xchg)
42         : "a" (oldvalue), "c" (newvalue), "d" (addr)
43     );
44     return xchg;
45 }
技術分享圖片

  android_atomic_cmpxchg是使用GNU C嵌入匯編實現,使用X86提供的對CAS的原子支持的指令。oldvalue放在eax寄存器中,newvalue放在ecx中,addr(pointer)放在edx中。cmpxchg指令首先比較addr指向的內存與oldvalue(eax),如果二者相等,將newvalue(ecx)放到addr所指向的內存中,同時設置Z標誌1。setne與andl 指令的操作的結果很簡單:如果Z標誌被設置,則eax為0,否則為1。程序執行最終eax放到xchg變量裏。

  可以看出自增、自減操作都是基於android_atomic_cmpxchg實現的,這個操作成功返回1,失敗返回0,例如:

int32_t a = 5;
int b = android_atomic_cmpxchg( a, a+1, &a );
printf("%d, %d\n", a, b);        
// 成功時輸出:6,1
// 失敗時輸出:5,0

1.2 iOS

  頭文件:#include <libkern/OSAtomic.h>

Operation

Function name

Description

Add

OSAtomicAdd32
OSAtomicAdd32Barrier
OSAtomicAdd64
OSAtomicAdd64Barrier

Adds two integer values together and stores the result in one of the specified variables.

Increment

OSAtomicIncrement32
OSAtomicIncrement32Barrier
OSAtomicIncrement64
OSAtomicIncrement64Barrier

Increments the specified integer value by 1.

Decrement

OSAtomicDecrement32
OSAtomicDecrement32Barrier
OSAtomicDecrement64
OSAtomicDecrement64Barrier

Decrements the specified integer value by 1.

Logical OR

OSAtomicOr32
OSAtomicOr32Barrier

Performs a logical OR between the specified 32-bit value and a 32-bit mask.

Logical AND

OSAtomicAnd32
OSAtomicAnd32Barrier

Performs a logical AND between the specified 32-bit value and a 32-bit mask.

Logical XOR

OSAtomicXor32
OSAtomicXor32Barrier

Performs a logical XOR between the specified 32-bit value and a 32-bit mask.

Compare and swap

OSAtomicCompareAndSwap32
OSAtomicCompareAndSwap32Barrier
OSAtomicCompareAndSwap64
OSAtomicCompareAndSwap64Barrier
OSAtomicCompareAndSwapPtr
OSAtomicCompareAndSwapPtrBarrier
OSAtomicCompareAndSwapInt
OSAtomicCompareAndSwapIntBarrier
OSAtomicCompareAndSwapLong
OSAtomicCompareAndSwapLongBarrier

Compares a variable against the specified old value. If the two values are equal, this function assigns the specified new value to the variable; otherwise, it does nothing. The comparison and assignment are done as one atomic operation and the function returns a Boolean value indicating whether the swap actually occurred.

Test and set

OSAtomicTestAndSet
OSAtomicTestAndSetBarrier

Tests a bit in the specified variable, sets that bit to 1, and returns the value of the old bit as a Boolean value. Bits are tested according to the formula (0×80 >> (n & 7)) of byte((char*)address + (n >> 3)) where n is the bit number and address is a pointer to the variable. This formula effectively breaks up the variable into 8-bit sized chunks and orders the bits in each chunk in reverse. For example, to test the lowest-order bit (bit 0) of a 32-bit integer, you would actually specify 7 for the bit number; similarly, to test the highest order bit (bit 32), you would specify 24 for the bit number.

Test and clear

OSAtomicTestAndClear
OSAtomicTestAndClearBarrier

Tests a bit in the specified variable, sets that bit to 0, and returns the value of the old bit as a Boolean value. Bits are tested according to the formula (0×80 >> (n & 7)) of byte((char*)address + (n >> 3)) where n is the bit number and address is a pointer to the variable. This formula effectively breaks up the variable into 8-bit sized chunks and orders the bits in each chunk in reverse. For example, to test the lowest-order bit (bit 0) of a 32-bit integer, you would actually specify 7 for the bit number; similarly, to test the highest order bit (bit 32), you would specify 24 for the bit number.

  這些操作中,可能關心的地方就是函數的返回值,這個返回值在編程的時看頭文件說明就好了,比較方便。官網文檔說明是這樣的:The arithmetic operations return the new value, after the operation has been performed. The boolean operations come in two styles, one of which returns the new value, and one of which (the "Orig" versions) returns the old. The compare-and-swap operations return true if the comparison was equal, ie if the swap occured. The bit test and set/clear operations return the original value of the bit. The dequeue operation returns the most recently enqueued element, or NULL if the list in empty.

1.3 Windows

  Windows平臺上的操作其實也是大體類似的,查看InterLockedIncrement、InterLockedDecrement系列的函數就知道了,需要註意的是這兩個函數返回操作後的新值,而不是操作前的原始值。

Header windows.h
Library coredll.lib
Windows Embedded CE Windows CE .NET 4.0 and later
Windows Mobile Windows Mobile Version 5.0 and later

多線程編程之原子操作