複数のクライアントからアクセスできる共有リソース(モーションシステム)があるアプリケーションがあります。移動中にシステムへのアクセスを必要とする個別の操作があり、競合する操作が同時に要求された場合は「ビジー」例外をスローする必要があります。他のアクションが散在するいくつかのオペレーションを実行するためにモーションシステムへの排他的アクセスを取得する必要があるシーケンサーもあります。シーケンス全体の間、他のクライアントはオペレーションを実行できません。
これまでは、スレッドアフィニティを使用してこれにアプローチしてきたため、スレッドは排他的アクセスを要求し、操作に対応するブロッキング呼び出しを実行できます。スレッドはアクセス権を持っていますが、他のスレッドはリソースを使用できません。私が今持っている問題は、よりきれいなシーケンサーの実装を可能にするために、非同期/待機パターンを使用してシステムを実装することに移行したことです。問題は、私のシーケンサーが常に同じスレッドで実行されているわけではないことです。アクティブスレッドはコールバック中に変更される可能性があるため、操作を実行し続けるために有効なコンテキストにいるかどうかを判断するのは容易ではありません。注目すべき点の1つは、一部の操作自体が待機で構成されていることです。つまり、シーケンスと個々の操作の両方が複数のスレッドにまたがることができます。
私の質問:非同期/待機によるスレッド切り替えがある場合に排他的アクセスを取得するのに適したパターンを知っている人はいますか?
参考までに、私が検討したいくつかのこと:
シーケンスの期間中、すべてのシーケンサー呼び出しを単一のスレッドにマーシャリングするカスタムSynchronizationContextを作成できます。これには、既存のスレッドアフィニティアクセス管理コードを再利用できるという利点があります。欠点は、シーケンスまたは操作を実行するたびにスレッドを専用にする必要があることです(操作は複数のスレッドにまたがることができるため)。
アクセスを取得したことを証明するために、Operationメソッドに渡す取得可能なアクセストークンを作成します。これには、トークンパラメータを使用してメソッドを膨らませるという欠点があります。
(2)のアクセストークンアプローチを使用しますが、Operationsインターフェイスの重複するインターフェイス実装を作成して、ラッパーがトークン「ベイクイン」でインスタンス化できるようにします。これは見苦しいグルーコードを作成しますが、シーケンサーコードをクリーンアップして、各メソッドにトークンを渡す必要がなくなります。
私の質問:非同期/待機によるスレッド切り替えが存在する場合に排他的アクセスを取得するための適切なパターンを知っている人はいますか?
はい、 AsyncLock
を使用できます。これは、私の AsyncExライブラリ の一部としても利用できます。 「TryLock」のような操作をしたい場合は、独自のプリミティブを作成する必要があります。
安全性チェックを実行する機能の一部が失われます。現在実行中のスレッドに特定のAsyncLock
があるかどうかをチェックする方法はありません。
その他のオプションには、ConcurrentExclusiveSchedulerPair
(私がブログ here についてブログ)またはTPL Dataflowが含まれます。
SemaphoreSlim.WaitAsync
これはここにぴったり収まります。 (私はそれを 同様の質問 で見つけました)。