如何阻止,直到事件被激发在C#
在提出这个问题之后 ,我想知道是否有可能等待一个事件被解雇,然后得到事件数据并返回它的一部分。 有点像这样:
private event MyEventHandler event; public string ReadLine(){ return event.waitForValue().Message; } ... event("My String"); ...elsewhere... var resp = ReadLine();
请确保你提供的任何解决scheme直接返回值,而不是从别的东西得到它。 我在问上面的方法是否以某种方式可用。 我知道Auto / ManuelResetEvent,但我不知道他们像上面那样直接返回值。
更新:我使用MyEventHandler
(其中包含Message
字段)声明了一个事件。 我有一个方法在另一个线程中调用ReadLine
等待事件触发。 事件触发时,WaitForValue方法(事件处理场景的一部分)返回包含消息的事件参数。 ReadLine将该消息返回给所有调用它的消息。
我问的那个问题的 答案就是我所做的,但是感觉不太对。 它几乎感觉到ManuelResetEvent触发和程序检索数据并返回它之间的数据可能会发生。
更新: Auto/ManualResetEvent
的主要问题是它太脆弱了。 一个线程可以等待这个事件,然后没有足够的时间让其他人获得它,然后再把它改成别的东西。 有没有办法使用锁或其他东西? 也许使用get和set语句。
您可以使用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 }
如果当前的方法是asynchronous的,那么你可以使用TaskCompletionSource。 创build一个事件处理程序和当前方法可以访问的字段。
TaskCompletionSource<bool> tsc = null; private async void Button_Click(object sender, RoutedEventArgs e) { tsc = new TaskCompletionSource<bool>(); await tsc.Task; WelcomeTitle.Text = "Finished work"; } private void Button_Click2(object sender, RoutedEventArgs e) { tsc?.TrySetResult(true); }
此示例使用具有名为WelcomeTitle的文本块和两个button的表单。 当第一个button被点击时,它开始点击事件,但停在等待线。 当第二个button被点击时,任务完成并且WelcomeTitle文本被更新。 如果你想暂停,然后改变
await tsc.Task;
至
await Task.WhenAny(tsc.Task, Task.Delay(25000)); if (tsc.Task.IsCompleted) WelcomeTitle.Text = "Task Completed"; else WelcomeTitle.Text = "Task Timed Out";
您可以等待的一种非常简单的事件是ManualResetEvent
,甚至更好的是ManualResetEventSlim
。
他们有一个WaitOne()
方法。 您可以永远等待,或者设置一个超时,或者一个“取消令牌”,这是您决定停止等待事件(如果您想取消您的工作,或者您的应用程序被要求退出)的一种方式。
你发射他们调用Set()
。
这是文档。
如果您很乐意使用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
消息来避免locking,但是这段代码表明它可以按预期工作。