基本的にロックの仕組みを尋ねているようです。ロックが構築されていない状態で、どうすればロックがアトミックな方法で内部状態を維持できるでしょうか? 最初は鶏が先か卵が先かの問題のように思えませんか?
この魔法はすべて、コンペア アンド スワップ(CAS) 操作によって実現されます。CAS 操作は、2 つの重要な処理を行うハードウェア レベルの命令です。
- メモリ バリアが生成されるため、命令の並べ替えが制限されます。
- メモリアドレスの内容を別の値と比較し、それらが等しい場合は元の値を新しい値に置き換えます。これはすべてアトミックに行われます。
最も基本的なレベルでは、これがトリックの達成方法です。別のスレッドが書き込み中に、他のすべてのスレッドが読み取りをブロックされているわけではありません。それはそれについて考える完全に間違った方法です。実際に起こることは、すべてのスレッドが同時にライターとして機能することです。戦略は悲観的よりも楽観的です。すべてのスレッドは、CAS と呼ばれるこの特殊な種類の書き込みを実行することによって、ロックを取得しようとします。実際には、Interlocked.CompareExchange
(ICX) メソッドを介して .NET の CAS 操作にアクセスできます。すべての同期プリミティブは、この 1 つの操作から構築できます。
のMonitor
ようなクラス (lock
キーワードが舞台裏で使用するクラス) を完全に C# でゼロから作成する場合は、Interlocked.CompareExchange
メソッドを使用してそれを行うことができます。これは非常に単純化された実装です。これは、.NET Framework が行う方法ではないことに注意してください。1以下のコードを提示する理由は、舞台裏で CLR マジックを必要とせずに純粋な C# コードで実行する方法を示し、Microsoft がそれをどのように実装したかについて考えさせるためです。
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
待機戦略は非常に単純化されていますが、BCL で既に実装されている実際のアルゴリズムに近似しています。Enter
上記の方法では、重要なビットを見つけやすくするために、意図的にコードを単純にしました。これは、私が通常これを実装する方法ではありませんが、重要な点を理解してくれることを願っています。
SimpleMonitor
また、上記には多くの問題があることに注意してください。ここに挙げたのはほんの一部です。
- ネストされたロックは処理しません。
- 実際のクラスのよう
Wait
なPulse
メソッドは提供しません。Monitor
それらを正しく行うのは本当に難しいです。
1 CLR は、各参照型に存在する特別なメモリ ブロックを実際に使用します。このメモリブロックは「同期ブロック」と呼ばれます。はMonitor
、このメモリ ブロック内のビットを操作して、ロックを取得および解放します。このアクションには、カーネル イベント オブジェクトが必要な場合があります。詳細については、Joe Duffy のブログを参照してください。