70

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

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

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

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

私が尋ねたその質問に対する受け入れられた答えは、私がしたことでしたが、それはまったく正しいとは思えませ. ManuelResetEvent が発火してから、プログラムがデータを取得して返すまでの間に、データに何かが起こりそうな気がします。

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

4

4 に答える 4

53

現在のメソッドが非同期の場合、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";
于 2016-07-06T14:00:35.903 に答える
45

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
}
于 2012-10-05T12:10:51.963 に答える
3

待機できる非常に簡単な種類のイベントは です。ManualResetEventさらに良いのはManualResetEventSlimです。

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

あなたはそれらを呼び出して発砲しますSet()

これがドキュメントです。

于 2012-10-05T12:12:10.107 に答える
1

Microsoft Reactive Extensions を使用することに満足している場合、これはうまく機能します。

public class Foo
{
    public delegate void MyEventHandler(object source, MessageEventArgs args);
    public event MyEventHandler _event;
    public string ReadLine()
    {
        return Observable
            .FromEventPattern<MyEventHandler, MessageEventArgs>(
                h => this._event += h,
                h => this._event -= h)
            .Select(ep => ep.EventArgs.Message)
            .First();
    }
    public void SendLine(string message)
    {
        _event(this, new MessageEventArgs() { Message = message });
    }
}

public class MessageEventArgs : EventArgs
{
    public string Message;
}

次のように使用できます。

var foo = new Foo();

ThreadPoolScheduler.Instance
    .Schedule(
        TimeSpan.FromSeconds(5.0),
        () => foo.SendLine("Bar!"));

var resp = foo.ReadLine();

Console.WriteLine(resp);

SendLineロックを回避するために別のスレッドでメッセージを呼び出す必要がありましたが、このコードは期待どおりに機能することを示しています。

于 2012-10-05T12:36:04.110 に答える