2011-02-03 15 views
1

誰も私に次の問題の良い解決策を教えてもらえますか?非同期リソースへのアクセスをシリアル化する方法は?

私が取り組んでいるアプリケーションは、TCPを介して他のシステムで動作するソフトウェアと通信する必要があります。そのシステムに送信する要求の中には、完了までに長時間(最大15秒)かかる場合があります。私のアプリケーションで

私は、リモート・システムと通信サービスにアクセスすることができ、メインUIスレッドを含むスレッドの数を、持っています。すべてのスレッドがアクセスするサービスのインスタンスは1つだけです。

一度に1つのリクエストしか処理できないようにする必要があります。つまり、シリアル化する必要があります。それ以外の場合は、TCP通信で問題が発生します。

未遂ソリューションこれまで

次のように当初、私は、各「コマンド」メソッドを保護するための静的オブジェクトで)(ロックを使用してみました:

lock (_cmdLock) 
{ 
SetPosition(position); 
} 

私はその時々それたがリモートシステムとTCP通信にタイムアウトがあっても、ロックを解放しません。さらに、同じスレッド(例えば、ユーザがボタンをダブルクリックした場合)から2つのコールが入った場合、ロックを過ぎてしまいます。ロックを再度読み取った後、同じスレッドがロックを待たないことがわかります。

次に、AutoResetEventsを使用して、一度に1つの通話のみを許可しようとしました。しかし、ロックがなければ、複数のスレッドでは動作しません。以下は、私は、コマンド要求のキューを実装しようとしていない私は(呼び出し元のスレッドからの)コマンドを送信し、(自身のスレッド上でバックグラウンドで実行されている)コマンド要求を処理するために使用されるコード

private static AutoResetEvent _cmdProcessorReadyEvent = new AutoResetEvent(false); 
    private static AutoResetEvent _resultAvailableEvent = new AutoResetEvent(false); 
    private static AutoResetEvent _sendCommandEvent = new AutoResetEvent(false); 

    // This method is called to send each command and can run on different threads 
    private bool SendCommand(Command cmd) 
    { 
     // Wait for processor thread to become ready for next cmd 
     if (_cmdProcessorReadyEvent.WaitOne(_timeoutSec + 500)) 
      { 
      lock (_sendCmdLock) 
       { 
       _currentCommand = cmd; 
       } 

      // Tell the processor thread that there is a command present 
      _sendCommandEvent.Set(); 

      // Wait for a result from the processor thread 
      if (!_resultAvailableEvent.WaitOne(_timeoutSec + 500)) 
       _lastCommandResult.Timeout = true; 

     } 
     return _lastCommandResult.Success; 
    } 

// This method runs in a background thread while the app is running 
private void ProcessCommand() 
    { 
     try 
     { 
      do 
      { 
       // Indicate that we are ready to process another commnad 
       _cmdProcessorReadyEvent.Set(); 

       _sendCommandEvent.WaitOne(); 
       lock (_sendCmdLock) 
       { 
        _lastCommandResult = new BaseResponse(false, false, "No Command"); 
        RunCOMCommand(_currentCommand); 
       } 

       _resultAvailableEvent.Set(); 

      } while (_processCommands); 
     } 
     catch (Exception ex) 
     { 
      _lastCommandResult.Success = false; 
      _lastCommandResult.Timeout = false; 
      _lastCommandResult.LastError = ex.Message; 
     } 

    } 

です呼び出しコードはすべてが同期することを期待しています。つまり、次のコマンドを送信する前に前のコマンドが完了していなければなりません。

追加の背景

リモートシステム上で動作するソフトウェアは、サードパーティの製品であり、私はそれへのアクセスを持っていない、統合されたXYテーブルとレーザーマーキングマシンを制御するために使用されます。

私は実際にレガシーVB6 DLLを使用してレーザーと通信しています。コマンドをフォーマットして応答を処理するためのすべてのコードが含まれています。このVB6 DLLは、通信にWinSockコントロールを使用します。

+1

レーザーを制御します!サメの頭に付いていますか?あなたはミニですか? ;) –

答えて

4

なぜキューイングの解決策が機能しないのか分かりません。

キューには、各リクエストと、結果を含むコールバックの詳細を入れないのはなぜですか?あなたのアプリケーションはこれらの要求を待ち行列に入れ、サードパーティのシステムに接続するモジュールは各キュー項目を順番に処理してを処理し、結果を返すことができます。

要求のディスパッチなどでロックを実装するのではなく、モジュール間の懸念をより明確に分離すると思います。リクエスタはシリアル化の制約をほとんど気にせず、サードパーティのインターフェイスモジュールはシリアル化を見たり、タイムアウトやその他のエラー等

編集:Javaの世界では、消費者/サイト運営者のために同期化されたBlockingQueuesがあり、このようなことは非常に簡単です。あなたがC#の世界で同じものを持っているかどうかはわかりません。クイック検索ではないと示唆していますが、この種のもののためにソースコードが浮かんでいます(C#世界の誰でも評価できる光を放つことができれば)

+0

私は完全にキューイングの解決策に同意します。 –

+0

はい、キューイングのソリューションを使用すると、待機中の要求の数を簡単に監視できるという利点があります。 –

+0

キューイングにはタイミングの問題があります。たとえば、レーザーを特定の位置に移動してからオンにし、RS232経由で別のコマンドを送信して、別のデバイスからレーザーのパワーを読み取る必要があります。リクエストをキューに入れると、いつ実行されるのかわからなくなり、パワーメータの読み値と調整することができません。 コマンドの中には、その後のコマンドで使用する必要があるときにデータを返すものがある(例:XY位置を取得する)ので、一度に1つのコマンドを実行する必要があります。 – canice

関連する問題