私のチームは、C# 5.0 で async/await を使用してマルチスレッド アプリケーションを開発しています。スレッド同期を実装する過程で、何度か繰り返した後、次のような内部ロックを備えた新しい SynchronizationContext 実装にたどり着きました。
- Post を呼び出す場合:
- ロックを取得できる場合、デリゲートはすぐに実行されます
- ロックを取得できない場合、デリゲートはキューに入れられます
- Send を呼び出す場合:
- ロックを取得できる場合、デリゲートが実行されます
- ロックを取得できない場合、スレッドはブロックされます
いずれの場合も、デリゲートを実行する前に、コンテキストはそれ自体を現在のコンテキストとして設定し、デリゲートが戻るときに元のコンテキストを復元します。
これは珍しいパターンであり、そのようなアプリケーションを作成したのは明らかに私たちが最初ではないので、私は疑問に思っています:
- パターンは本当に安全ですか?
- スレッド同期を実現するためのより良い方法はありますか?
SerializingSynchronizationContext のソースとGitHubのデモを次に示します。
使用方法は次のとおりです。
- 保護が必要な各クラスは、ミューテックスのようなコンテキストの独自のインスタンスを作成します。
コンテキストは awaitable であるため、次のようなステートメントが可能です。
await myContext;
これにより、残りのメソッドがコンテキストの保護下で実行されるようになります。
- クラスのすべてのメソッドとプロパティは、このパターンを使用してデータを保護します。await の間では、一度に 1 つのスレッドのみがコンテキストで実行できるため、状態は一貫したままになります。await に到達すると、次にスケジュールされたスレッドがコンテキストで実行できるようになります。
- 待機中の式でも原子性を維持する必要がある場合は、カスタム SynchronizationContext を AsyncLock と組み合わせて使用できます。
- クラスの同期メソッドは、保護のためにカスタム コンテキストを使用することもできます。