web-dev-qa-db-ja.com

C#でイベントが発生するまでブロックする方法

この質問 を尋ねた後、イベントが発生するのを待ってから、イベントデータを取得してその一部を返すことが可能かどうか疑問に思っています。このような並べ替え:

private event MyEventHandler event;
public string ReadLine(){ return event.waitForValue().Message; }
...
event("My String");
...elsewhere...
var resp = ReadLine();

他のソリューションから値を取得するのではなく、提供するソリューションが値を直接返すことを確認してください。上記の方法が何らかの方法で利用可能かどうかを尋ねています。 Auto/ManuelResetEventについては知っていますが、上記のように値が直接返されることはわかりません。

更新:MyEventHandlerMessageフィールドを含む)を使用してイベントを宣言しました。別のスレッドにReadLineというメソッドがあり、イベントの発生を待機しています。イベントが発生すると、WaitForValueメソッド(イベント処理シーンの一部)がメッセージを含むイベント引数を返します。その後、メッセージはReadLineによって、呼び出し元に返されます。

受け入れられた答え から その質問 私は私がしたことを尋ねましたが、それはまったく正しく感じられません。 ManuelResetEventが起動してからプログラムがデータを取得して返すまでの間に、データに何かが起こる可能性がほとんどあります。

更新:Auto/ManualResetEventの主な問題は、脆弱性が大きすぎることです。スレッドはイベントを待つことができますが、他の誰かにイベントを変更する前に、他の誰かがそれを取得するのに十分な時間を与えることができません。ロックなどを使用する方法はありますか? getおよびsetステートメントを使用している可能性があります。

53
Arlen Beiler

ManualResetEvent を使用できます。セカンダリスレッドを起動する前にイベントをリセットし、 WaitOne() メソッドを使用して現在のスレッドをブロックします。その後、セカンダリスレッドにManualResetEventを設定させると、メインスレッドが継続します。このようなもの:

ManualResetEvent oSignalEvent = new ManualResetEvent(false);

void SecondThread(){
    //DoStuff
    oSignalEvent.Set();
}

void Main(){
    //DoStuff
    //Call second thread
    System.Threading.Thread oSecondThread = new System.Threading.Thread(SecondThread);
    oSecondThread.Start();

    oSignalEvent.WaitOne(); //This thread will block here until the reset event is sent.
    oSignalEvent.Reset();
    //Do more stuff
}
38
Vinoth

現在のメソッドが非同期の場合、TaskCompletionSourceを使用できます。イベントハンドラーと現在のメソッドがアクセスできるフィールドを作成します。

    TaskCompletionSource<bool> tcs = null;

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        tcs = new TaskCompletionSource<bool>();
        await tcs.Task;
        WelcomeTitle.Text = "Finished work";
    }

    private void Button_Click2(object sender, RoutedEventArgs e)
    {
        tcs?.TrySetResult(true);
    }

この例では、WelcomeTitleという名前のテキストブロックと2つのボタンを持つフォームを使用します。最初のボタンをクリックすると、クリックイベントが開始されますが、待機行で停止します。 2番目のボタンをクリックすると、タスクが完了し、WelcomeTitleテキストが更新されます。タイムアウトする場合も変更します

await tcs.Task;

await Task.WhenAny(tcs.Task, Task.Delay(25000));
if (tcs.Task.IsCompleted)
    WelcomeTitle.Text = "Task Completed";
else
    WelcomeTitle.Text = "Task Timed Out";
32
Adam

待つことができる非常に簡単なイベントはManualResetEventで、さらに良いのはManualResetEventSlimです。

それらには、まさにそれを行うWaitOne()メソッドがあります。永遠に待つか、タイムアウト、またはイベントの待機を停止することを決定する方法である「キャンセルトークン」を設定できます(作業をキャンセルする場合、またはアプリの終了を要求される場合)。

Set()を呼び出して起動します。

ここ はドキュメントです。

3
mbarthelemy