C++で記述された外部dllを使用するC#で記述されたWebアプリケーションがあります。クライアント(Webブラウザー)とWebアプリケーション間の通信は、SignalRを使用して行われます。 Webアプリケーションとdllの間の通信には、クラスを使用しており、接続されているすべてのクライアントに対してこのクラスのインスタンスを作成します。このクラスのインスタンスは静的リストに保存されます-リクエスト間で共有されます。
リクエストが発行されたら、リストからコネクタ(dllと通信するクラス)をフェッチしてから、0.1秒から3秒の間の任意の時間を取ることができるメソッドを呼び出します。
それに加えて、(Hangfireを使用して)毎分トリガーされるジョブがあり、リストをループして、特定の時間非アクティブなコネクターを破棄します。コネクタの廃棄にも約1〜2秒かかる場合があります。
アプリケーションはマルチスレッドであるため、コネクタクラスとコネクタを破棄するジョブの間で共有ロックを使用しています。ロックを使用すると、コネクタをフェッチしてからメソッドを呼び出すまでの間にコネクタが配置されないようにします。
「疑似コード」では、次のようになります。
object globalLock = new object();
-- signalr hub method
public void HubMethod(string sessionId, string param) {
lock (globalLock) {
DllConnector connector = GetConnector(sessionId);
connector.CallMethod(param);
}
}
-- hangfire job
public void KillInactive() {
lock (globalLock) {
foreach (DllConnector connector in listOfConnectors) {
if (connector.IsInactive()) {
listOfConnectors.Remove(connector);
connector.Dispose();
}
}
}
}
質問:
「多数の」同時クライアント(リクエスト)が予想されるため、これは拡張できないのではないかと心配しています。リクエストが発生すると、ジョブが応答しなくなります。これを回避するにはどうすればよいですか?ロックの待機を最小限に抑え、メソッドのフェッチと呼び出しの間にコネクタが破棄されないようにします。パターンはありますか?
これらが通常行われる方法は、スレッドプールを使用することです。 _System.Threading.ThreadPool
_を参照し、 MSDN:方法:スレッドプールを使用する を読んでください。
また、マルチスレッド環境でデータを共有するための最新のアプローチは、ロックの使用を避け、代わりにメッセージパッシングを使用することです。これにより、テスト可能なシステムが実現し、動作が確認された後は、まれなタイミングの問題による厄介な驚きなしに動作し続ける傾向があります。
注:listOfConnectors.Remove( x );
内でforeach( var x in listOfConnectors )
を呼び出すと、「コレクションが変更されました」という例外がスローされます。