在询问this question后,我想知道是否可以等待事件被触发,然后获取事件数据并返回部分事件。有点像这样:
private event MyEventHandler event;
public string ReadLine(){ return event.waitForValue().Message; }
...
event("My String");
...elsewhere...
var resp = ReadLine();
请确保您提供的任何解决方案直接返回值,而不是从其他内容获取。我想问一下上面的方法是否有用。我知道Auto / ManuelResetEvent,但我不知道他们像我上面那样直接返回值。
更新:我使用MyEventHandler
(包含Message
字段)声明了一个事件。我在另一个名为ReadLine
的线程中有一个方法,等待事件触发。当事件触发WaitForValue方法(事件处理场景的一部分)时,返回包含消息的事件args。然后,ReadLine将消息返回给任何调用它的消息。
The accepted answer致that question我问的是我做了什么,但它感觉不太对劲。几乎感觉ManuelResetEvent触发和检索数据并返回它的程序之间的数据可能会发生。
更新: Auto/ManualResetEvent
的主要问题是它太脆弱了。线程可以等待事件,然后在将其更改为其他内容之前,没有足够的时间让其他人获取它。有没有办法使用锁或其他东西?也许使用get和set语句。
答案 0 :(得分:37)
您可以使用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
}
答案 1 :(得分:30)
如果当前方法是异步的,那么您可以使用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的文本块和两个按钮的表单。单击第一个按钮时,它会启动单击事件,但会在等待行停止。单击第二个按钮时,任务完成并更新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";
答案 2 :(得分:2)
您可以等待的非常简单的事件是ManualResetEvent
,甚至更好,ManualResetEventSlim
。
他们有WaitOne()
方法就是这么做的。您可以永远等待,或者设置超时或“取消令牌”,这是您决定停止等待活动的一种方式(如果您要取消工作,或者要求您的应用退出)。
你打电话给他们Set()
。
Here是文档。
答案 3 :(得分:-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
消息以避免锁定,但此代码显示它按预期工作。