8

私のチームは、C# 5.0 で async/await を使用してマルチスレッド アプリケーションを開発しています。スレッド同期を実装する過程で、何度か繰り返した後、次のような内部ロックを備えた新しい SynchronizationContext 実装にたどり着きました。

  1. Post を呼び出す場合:
    • ロックを取得できる場合、デリゲートはすぐに実行されます
    • ロックを取得できない場合、デリゲートはキューに入れられます
  2. Send を呼び出す場合:
    • ロックを取得できる場合、デリゲートが実行されます
    • ロックを取得できない場合、スレッドはブロックされます

いずれの場合も、デリゲートを実行する前に、コンテキストはそれ自体を現在のコンテキストとして設定し、デリゲートが戻るときに元のコンテキストを復元します。

これは珍しいパターンであり、そのようなアプリケーションを作成したのは明らかに私たちが最初ではないので、私は疑問に思っています:

  1. パターンは本当に安全ですか?
  2. スレッド同期を実現するためのより良い方法はありますか?

SerializingSynchronizationContext のソースとGitHubのデモを次に示します。

使用方法は次のとおりです。

  • 保護が必要な各クラスは、ミューテックスのようなコンテキストの独自のインスタンスを作成します。
  • コンテキストは awaitable であるため、次のようなステートメントが可能です。

    await myContext;

    これにより、残りのメソッドがコンテキストの保護下で実行されるようになります。

  • クラスのすべてのメソッドとプロパティは、このパターンを使用してデータを保護します。await の間では、一度に 1 つのスレッドのみがコンテキストで実行できるため、状態は一貫したままになります。await に到達すると、次にスケジュールされたスレッドがコンテキストで実行できるようになります。
  • 待機中の式でも原子性を維持する必要がある場合は、カスタム SynchronizationContext を AsyncLock と組み合わせて使用​​できます。
  • クラスの同期メソッドは、保護のためにカスタム コンテキストを使用することもできます。
4

2 に答える 2

4

一度に複数の操作を実行しない同期コンテキストを持つことは、確かに目新しいことではなく、まったく悪いことでもありません。 ここでは、2 年前にスティーブン・トゥーブが作り方を説明しているのを見ることができます。(この場合、単にメッセージ ポンプを作成するためのツールとして使用されています。実際には、まさにそれが必要なように思えますが、そうでない場合でも、同期コンテキストをソリューションから引き出して、個別に使用することができます。)

もちろん、単一のスレッド化された同期コンテキストを持つことは、完全に概念的に理にかなっています。UI の状態を表す同期コンテキストはすべてこのようなものです。winforms、WPF、winphone などの同期コンテキストはすべて、そのコンテキストからの単一の操作のみが一度に実行されることを保証します。

1 つの心配な点は次のとおりです。

いずれの場合も、デリゲートを実行する前に、コンテキストはそれ自体を現在のコンテキストとして設定し、デリゲートが戻るときに元のコンテキストを復元します。

コンテキスト自体がこれを行うべきではないと思います。呼び出し元がこの同期コンテキストを現在のコンテキストにしたい場合は、それを設定できます。現在のコンテキスト以外で使用したい場合は、許可する必要があります。特定のリソースへのアクセスを同期するために、現在のコンテキストとして設定せずに同期コンテキストを使用したい場合があります。そのような場合、特にそのリソースにアクセスする操作のみが、このコンテキストを使用する必要があります。

于 2014-01-08T15:36:10.520 に答える