它看起來像你基本上是問如何鎖工作。鎖怎樣才能以原子的方式保持內部狀態而不必建立鎖?它似乎是一個chicken and egg problem起初不是嗎?
這一切都是因爲compare-and-swap(CAS)的操作而發生的。 CAS操作是一個硬件級別的指令,它執行兩個重要的事情。
- 它生成一個內存屏障,以便指令重新排序受到約束。
- 它將內存地址的內容與另一個值進行比較,如果它們相等,則將原始值替換爲新值。它以原子的方式完成所有這些。
從最基本的層面來看,這就是技巧的完成。並非所有其他線程都在閱讀時被阻止,而另一線程正在寫入。這完全是錯誤的思考方式。實際發生的情況是所有線程都同時作爲編寫者。該策略比悲觀更樂觀。每個線程都試圖通過執行稱爲CAS的特殊類型的寫入來獲取鎖。您實際上可以通過Interlocked.CompareExchange
(ICX)方法在.NET中訪問CAS操作。每一個同步原語都可以從這個單獨的操作中建立。
如果我要在C#中完全從頭開始編寫類似Monitor
的類(即關鍵字lock
在幕後使用的),我可以使用Interlocked.CompareExchange
方法來完成。這是一個過度簡化的實現。請記住,這當然是而不是 .NET Framework如何做到這一點。 我提供下面的代碼的原因是向您展示如何在純粹的C#代碼中完成而不需要幕後的CLR魔術,以及它可能讓您考慮微軟如何實施它。
public class SimpleMonitor
{
private int m_LockState = 0;
public void Enter()
{
int iterations = 0;
while (!TryEnter())
{
if (iterations < 10) Thread.SpinWait(4 << iterations);
else if (iterations % 20 == 0) Thread.Sleep(1);
else if (iterations % 5 == 0) Thread.Sleep(0);
else Thread.Yield();
iterations++;
}
}
public void Exit()
{
if (!TryExit())
{
throw new SynchronizationLockException();
}
}
public bool TryEnter()
{
return Interlocked.CompareExchange(ref m_LockState, 1, 0) == 0;
}
public bool TryExit()
{
return Interlocked.CompareExchange(ref m_LockState, 0, 1) == 1;
}
}
該實現演示了一些重要的事情。
- 它顯示瞭如何使用ICX操作來自動讀取和寫入鎖定狀態。
- 它顯示了可能發生的等待情況。
注意我是如何使用Thread.SpinWait
,Thread.Sleep(0)
,Thread.Sleep(1)
和Thread.Yield
而鎖等待被收購。等待策略過於簡化,但它的確已經接近real life algorithm implemented in the BCL。我故意在上面的Enter
方法中保持代碼簡單,以便更容易發現關鍵位。這不是我通常會如此實施的方式,但我希望它能夠將重點放在首位。
另請注意,我的SimpleMonitor
上面有很多問題。這只是少數。
- 它不處理嵌套鎖定。
- 它不提供
Wait
或Pulse
方法像真正的Monitor
類。他們真的很難做對。
的CLR將實際使用的存儲器的特殊塊上的每個引用類型存在。這塊內存被稱爲「同步塊」。 Monitor
將操縱這塊內存中的位來獲取和釋放鎖。這個動作可能需要一個內核事件對象。你可以閱讀關於Joe Duffy's blog的更多信息。
查看另一個類似的問題在這裏:http://stackoverflow.com/questions/14758088/how-are-atomic-operations-implemented-at-a-hardware-level – blt
他們是原子..具體來說,他們是一個原子更新的同步塊索引..這只是一個整數。 –
這是鎖定的關鍵 - 以便您可以編寫安全的併發線程使用的代碼。 – Enigmativity