SpinLock構造体

List<int> list = new List<int>(); // 保護するリソース

SpinLock spinLock = new SpinLock();
Action action = () =>
{
    for (int i = 0; i < 10000; i++)
    {
        bool lockTaken = false;
        try
        {
            spinLock.Enter(ref lockTaken);

            list.Add(i);
        }
        finally
        {
            if (lockTaken) spinLock.Exit();
        }
    }
};

Parallel.Invoke(action, action, action, action);
例 - SpinLock 構造体 (System.Threading) | Microsoft Learn

Monitorとの比較

  • SpinLockでは、Monitorを使用することによる暗黙的なオブジェクトの割り当てを回避できる。
  • SpinLockはリーフレベル (leaf-level) のロック、つまりリンクリストのノードごとのロックのように、ロックの保持時間が短い場合に有益。

SpinLockは、アプリケーションのパフォーマンスが向上する場合にのみ使用するようにします。

Remarks - SpinLock Struct (System.Threading) | Microsoft Learn
SpinLock Monitor
SpinLock spinLock = new SpinLock();

bool lockTaken = false;
try
{
    spinLock.Enter(ref lockTaken);
    // 共有リソースへのアクセス
}
finally
{
    if (lockTaken) spinLock.Exit();
}
object sync = new object();

bool lockTaken = false;
try
{
    Monitor.Enter(sync, ref lockTaken);
    // 共有リソースへのアクセス
}
finally
{
    if (lockTaken) Monitor.Exit(sync);
}

コンストラクタ

public SpinLock (bool enableThreadOwnerTracking);
SpinLock(Boolean) Constructor (System.Threading) | Microsoft Learn

enableThreadOwnerTrackingをtrueとすると、デバッグのためにスレッドIDを使用するようになります。これを有効にするとスレッド追跡モード (Thread-Tracking Mode) となり、同一のスレッドでロックを再取得しようとすると例外が投げられるようになります。スレッド追跡モード - 方法: SpinLock のスレッド追跡モードを有効にする | Microsoft Learn

引数のないコンストラクタでは、このenableThreadOwnerTrackingは既定でtrueとなります。またこの値は、IsThreadOwnerTrackingEnabledプロパティで確認できます。

SpinLock spinLock1 = new SpinLock(false);

bool lockTaken = false;
spinLock1.Enter(ref lockTaken);

lockTaken = false;
spinLock1.Enter(ref lockTaken); // デッドロックする
SpinLock spinLock2 = new SpinLock();

bool lockTaken = false;
spinLock2.Enter(ref lockTaken);

lockTaken = false;
spinLock2.Enter(ref lockTaken); // LockRecursionException「呼び出しスレッドは既にロックを保持しています。」

メソッド

TryEnter()

信頼できる方法でロックを取得できます。

public void TryEnter (
    int millisecondsTimeout, // 待機時間
    ref bool lockTaken       // ロックを得られたらtrue
    );
TryEnter(Int32, Boolean) - SpinLock.TryEnter Method (System.Threading) | Microsoft Learn

ロックを入手できるか、millisecondsTimeoutが終了するまでブロックされます。

Exit()

ロックを解放できます。

public void Exit (
    bool useMemoryBarrier // trueならば、終了を他のスレッドにただちに公開するためのメモリフェンス (memory fence / memory barrier) を発行する
    );
Exit(Boolean) - SpinLock.Exit Method (System.Threading) | Microsoft Learn

useMemoryBarrierをtrueとすると、パフォーマンスを費やしてロックの公平性を向上させます。既定のExit()のオーバーロードでは、useMemoryBarrierにtrueを指定したように動作します。

Microsoft Learnから検索