2011-05-02 50 views
5

我想使無鎖使用互鎖操作,以下任何想法如何翻譯此代碼片段?無鎖使用聯鎖CompareExchange

if (m_Ref == 0xFFFF) 
    m_Ref = 1; 
else 
{ 
    if (++m_Ref == 1) 
     CallSomething(); // 

} 

我想這樣

if (InterlockedCompareExchange(&m_Ref, 1, 0xFFFF) != 0xFFFF)) 
{ 
    if (InterlockedIncrement(&m_Ref) == 1) 
     CallSomething(); 
} 

是否有這個的任何問題/種族?

回答

8

這看起來很正常,但每次在連續使用兩個互鎖操作時,您都會暴露於ABA problem。在這種情況下,一個線程無法將其從0xFFFF更改爲1(ICX返回!=0xFFFF),因此它繼續並採用if分支並將其增加。在運行InterlockedIncrement之前,另一個線程將m_ref更改回0xFFFF,並且原始線程遞增0xFFFF。取決於m_ref的類型/語義,效果會很謹慎,但肯定會很糟糕。

你應該做一個單一的ICX操作,同時爲0xFFF的1和X到X + 1,始終重試,如果你輸了ICX:

volatile <type> m_ref; 

<type> ref, newRef, icxref; 
do 
{ 
    ref = m_ref; 
    newRef = (0xFFFF == ref) ? 1 : ++ref; 
    icxref = InterlockedCompareExchange (&m_ref, newRef, ref); 
} while (icxref != ref); 
if (newRef == 1 && ref != 0xFFFF) 
{ 
    DoSomething(); 
} 
+0

@Remus Rusanu - 非常感謝 – Suresh 2011-05-02 23:03:05

+0

這裏沒有使用'volatile'的點,它沒有映射內存。 – GManNickG 2011-05-03 01:43:30

+0

@GMan:內存映射與'volatile'有什麼關係? – 2011-05-03 04:04:43

4

是的,有一場比賽。另一種情況可能會在InterlockedCompareExchangeInterlockedIncrement之間做很多

+0

謝謝你,我的想法一樣,你有任何想法如何解決這個問題? – Suresh 2011-05-02 22:45:44

+0

@Suresh:並非如此,如果你想讓它無鎖。如果您可以更改爲允許0,那麼在'InterlockedIncrement'返回值上使用'%' – Erik 2011-05-02 22:49:45

+0

@Suresh如果您不希望種族在實際運行代碼中彈出太多,則可以使用InterlockedCompareExchange作爲樂觀鎖定機制。 – 2011-05-02 22:58:20