2016-07-24 18 views
2

私のアプリケーションは、TCP/IPプロトコルとCOMポートを介して多くの外部デバイスに接続されています。メインロジックは、外部デバイスからの信号をリッスンしてコマンドを送信する(有限状態マシンとして実装された)MainControllerクラスにあります。同期メソッドを非同期で実行し、結果を待つ方法

MainControllerが外部信号を受信するたびに、GUIにイベント、たとえばOnSensor1ReceivedOnSensor2Receivedなどが通知されます。アイコンやメッセージなどを表示できます。他の方向にも同じロジックが適用されます。MainControllerが外部デバイスに信号を送信すると、イベントが発生し、GUIに何かが表示されます。

ここまでのところ、すべての通信は非同期です(これが正しい用語であれば)。つまり、イベントを受け取って処理し、コマンドを実行しますが、同じ操作でリターンを待つことはありません(コマンドを送信するすべてのメソッドが無効であると言うこともできます)。

しかし、今では1つのパブリックメソッドint Process(string input, out string output)を持つ新しいデバイスを追加する必要があります。メソッドの実行には数秒かかりますが、その間に他のすべてのコードが待機しています。たとえば、このメソッドの実行中に別の外部デバイスから信号が取得された場合、GUIはブロック後にのみリフレッシュされますメソッドWriteが実行されます。 Writeメソッドを非同期に実行させると、GUIは他のすべての信号をリアルタイムで表示しますか?私は新しいデバイスを使用しようとすると

のコード例では、これはどこかMainControllerで呼ばれている:

// Notify GUI that the blocking plugin was activated. 
OnBlockingPluginActive(this, EventArgs.Empty); 

string result; 
// Here is the problem - during the execution of this method the GUI is not responding for other signals. 
var status = blockingPlugin.Process(input, out result); 
if (status == 0) 
{ 
    // OK, notify GUI and fire a BlockingPluginOK command in finite state machine. 
    OnBlockingPluginOK(this, EventArgs.Empty); 
} 
else 
{ 
    // Error, notify GUI and fire a BlockingPluginError command in finite state machine. 
    OnBlockingPluginError(this, EventArgs.Empty); 
} 

また、私は、そう何のネイティブサポートを.NET 4.0を使用していないしていますし、4.5にアップグレードすることはできませんのでご注意くださいasync/awaitの場合。

+3

あなたのバンは、これを試してください:[ https://www.nuget.org/packages/Microsoft.Bcl.Async/1.0.16](https://www.nuget.org/packages/Microsoft.Bcl.Async/1.0.16) – Fabio

+2

まだ.NET 4.0を使用して、Task.Factory.StartNewを使用して、.ContinueWith –

+0

"秒数秒"を使用するのは合理的な動作ではありません。呼び出しをワーカースレッドに移動することはできますが、呼び出しをブロックしてブロックします。そのライブラリは大幅にボルケンであるか、環境問題のために正しく動作していません。あなたは、問題を回避しようとすることによってそれをより良くするつもりはない、アドバイスのために図書館の著者にpingをする。 –

答えて

1

あなたはAsyncCallbackBeginInvoke/EndInvoke経由Asynchronous delegate call昔ながらを使用して、変数をキャプチャすることができます

// Notify GUI that the blocking plugin was activated. 
OnBlockingPluginActive(this, EventArgs.Empty); 

string result; 
Func<int> processAsync =() => blockingPlugin.Process(input, out result); 
processAsync.BeginInvoke(ar => 
{ 
    var status = processAsync.EndInvoke(ar); 
    if (status == 0) 
    { 
     // OK, notify GUI and fire a BlockingPluginOK command in finite state machine. 
     OnBlockingPluginOK(this, EventArgs.Empty); 
    } 
    else 
    { 
     // Error, notify GUI and fire a BlockingPluginError command in finite state machine. 
     OnBlockingPluginError(this, EventArgs.Empty); 
    } 
}, null); 
+0

ありがとう、ちょうど私が探していたもの。 – sventevit

1

メインコントローラは、それがイベントにGUIを通知外部からの信号を受信するたびに...

を。 ..

ここまでのすべての通信は非同期です...

すでに非同期です。 TaskまたはIObservableではなく、イベントを使用して補完を通知するだけです。一般的なイベントは、コードを消費しています(つまり、コードを多くのメソッドに渡し、独自の状態オブジェクトを明示的に管理する必要があります)。実際にはの非同期です。

Writeメソッドを非同期に実行すると、GUIで他のすべての信号がリアルタイムで表示されます。

「非同期に実行する」ことはできません。「非同期的に」適切な意味ではありません。現在、Writeメソッドは、呼び出し元のスレッドをブロックします(つまり、同期しています)。魔法のように、呼び出しスレッド(つまり、非同期)をブロックできないメソッドを呼び出す方法はありません。

しかし、私は "偽の非同期"と呼ばれる手法を使用することができます。基本的に、UIスレッドのように見せかけますが、その操作はスレッドプールスレッド上で実行することによって非同期です。操作はまだブロックされていますが、UIスレッドではなくスレッドプールスレッドをブロックします。

あなたがMicrosoft.Bcl.Asyncを使用することができるなら、あなたはこのようなコード記述することができます。

try 
{ 
    var result = await TaskEx.Run(() => 
    { 
    string result; 
    if (blockingPlugin.Process(input, out result) != 0) 
     throw new Exception("Blocking plugin failed."); 
    return result; 
    }); 
    OnBlockingPluginOK(this, EventArgs.Empty); 
} 
catch 
{ 
    OnBlockingPluginError(this, EventArgs.Empty); 
} 

はそうでない場合、あなたはそれを古い学校を行う必要があるでしょう:

var ui = TaskScheduler.FromCurrentSynchronizationContext(); 
var task = Task.Factory.StartNew(() => 
{ 
    string result; 
    if (blockingPlugin.Process(input, out result) != 0) 
    throw new Exception("Blocking plugin failed."); 
    return result; 
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); 
task.ContinueWith(t => OnBlockingPluginOK(this, EventArgs.Empty), 
    TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.OnlyOnRanToCompletion, 
    ui); 
task.ContinueWith(t => OnBlockingPluginError(this, EventArgs.Empty), 
    TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.NotOnRanToCompletion, 
    ui); 
+0

ありがとうございます。 @Ivan Stoevのソリューション(http://stackoverflow.com/a/38551022/113858)にコメントできますか?私はまずそれを試してみて、それがうまくいっているように見えます。 – sventevit

+1

@sventevit: 'BeginInvoke'はスレッドプールスレッドも使用します。ただし、そのコード例では、UIスレッドではなくスレッドプールスレッドで 'OnBlockingPluginOK'と' OnBlockingPluginError'を発生させます。 –

+0

それは私が推測する良いことですか? – sventevit

関連する問題