スレッド処理のデザイン ガイドライン

次の規則は、スレッド処理を実装するときのデザイン ガイドラインを示しています。

  • 静的状態を変更する静的メソッドは提供しないでください。一般的なサーバーのシナリオでは、静的状態は要求間で共有されます。つまり、複数のスレッドがそのコードを同時に実行できます。これにより、スレッド処理のバグが発生する可能性が高くなります。要求間で共有されないインスタンスにデータをカプセル化するデザイン パターンの使用を検討してください。

  • 静的状態はスレッド セーフである必要があります。

  • インスタンス状態はスレッド セーフである必要がありません。既定では、クラス ライブラリはスレッド セーフではないことが必要です。スレッド セーフなコードを作成するロックを追加すると、パフォーマンスが低下し、ロックの競合が増加し、デッドロックのバグが発生する可能性が生じます。一般的なアプリケーション モデルでは、一度にユーザー コードを実行するスレッドは 1 つだけにして、スレッド セーフを実現する必要を最小限に抑えます。この理由から、.NET Framework のクラス ライブラリは既定ではスレッド セーフではないことが必要です。スレッド セーフなバージョンを提供する必要がある場合は、型のスレッド セーフなインスタンスを返す Synchronized メソッドを用意します。例については、System.Collections.ArrayList.Synchronized メソッドSystem.Collections.ArrayList.IsSynchronized メソッドを参照してください。

  • サーバーのシナリオで実行するときの負荷を考慮して、ライブラリをデザインします。可能な限り、ロックの取得は避けます。

  • ロックされたセクションでのメソッド呼び出しには注意が必要です。A クラスの静的メソッドが B クラスの静的メソッドを呼び出す場合、およびその逆の場合に、デッドロックが発生する可能性があります。A と B の両方で静的メソッドを同期させている場合には、デッドロックが発生します。このデッドロックは、スレッドの負荷が高い場合にだけ検出されます。

  • A クラスの静的メソッドが A クラスの静的メソッドを呼び出す場合は、パフォーマンスの問題が発生することがあります。これらのメソッドが正しく考慮されていないと、冗長な同期が大量に発生するため、パフォーマンスが損なわれます。細分化された同期を使用しすぎると、パフォーマンスに悪影響を与えます。また、スケーラビリティにも深刻な悪影響を与えることになります。

  • lock ステートメント (Visual Basic では SyncLock) を使用する場合には注意が必要です。lock ステートメントを使用すると、スレッド処理のすべての問題が解決します。しかし、アトミック実行の必要がある更新の場合には、System.Threading.Interlocked クラスの方が優れています。競合がない場合は、単一の lock プリフィックスが実行されます。コード レビューでは、次の例で示すようなインスタンスに注意する必要があります。

    SyncLock Me
       myField += 1
    End SyncLock
    [C#]
    lock(this) 
    {
       myField++;
    }
    

    前の例を次の例で置き換えると、パフォーマンスが向上します。

    System.Threading.Interlocked.Increment(myField)
    [C#]
    System.Threading.Interlocked.Increment(myField);
    

    オブジェクト型の変数が null (Visual Basic では Nothing) のときだけに、これを更新する例を示します。次のコードを使用すると、変数を更新し、コードをスレッド セーフにすることができます。

    If x Is Nothing Then
       SyncLock Me
          If x Is Nothing Then
             x = y
          End If
       End SyncLock
    End If
    [C#]
    if (x == null)
    {
       lock (this)
       {
          if (x == null)
          {
             x = y;
          }
       }
    }
    

    前の例を次のコードで置き換えると、パフォーマンスが向上します。

    System.Threading.Interlocked.CompareExchange(x, y, Nothing)
    [C#]
    System.Threading.Interlocked.CompareExchange(ref x, y, null);
    
  • 可能な限り、同期の必要を避けるようにします。トラフィック量が多い経路では、同期を避けた方が賢明です。場合によっては、競合状態をなくすのではなく、許容するようにアルゴリズムを調整できます。

参照

クラス ライブラリ開発者向けのデザイン ガイドライン | Visual Basic の言語の変更 | System.Threading 名前空間