如何在c#中阻塞直到事件被触发
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12745848/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to block until an event is fired in c#
提问by Arlen Beiler
After asking this question, I am wondering if it is possible to wait for an event to be fired, and then get the event data and return part of it. Sort of like this:
问这个问题后,我想知道是否可以等待一个事件被触发,然后获取事件数据并返回其中的一部分。有点像这样:
private event MyEventHandler event;
public string ReadLine(){ return event.waitForValue().Message; }
...
event("My String");
...elsewhere...
var resp = ReadLine();
Please make sure whatever solution you provide returns the value directly rather than getting it from something else. I'm asking if the method above is available in some way. I know about Auto/ManuelResetEvent, but I don't know that they return the value directly like I did above.
请确保您提供的任何解决方案直接返回值,而不是从其他地方获取。我在问上面的方法是否以某种方式可用。我知道 Auto/ManuelResetEvent,但我不知道它们像我上面那样直接返回值。
Update:I declared an event using MyEventHandler(which contains a Messagefield). I have a method in another thread called ReadLinewaiting for the event to fire. When the event fires the WaitForValue method (part of the event handling scene) returns the event args, which contains the message. The message is then returned by ReadLine to whatever had called it.
更新:我使用MyEventHandler(其中包含一个Message字段)声明了一个事件。我在另一个线程中有一个方法,称为ReadLine等待事件触发。当事件触发 WaitForValue 方法(事件处理场景的一部分)时,返回包含消息的事件参数。然后,该消息由 ReadLine 返回给调用它的任何对象。
The accepted answerto that questionI asked was what I did, but it just doesn't feel quite right. It almost feels like something could happen to the data between the ManuelResetEvent firing and the program retrieving the data and returning it.
接受的答案,以这个问题我问的是我做什么,但它只是感觉并不完全正确。几乎感觉在 ManuelResetEvent 触发和程序检索数据并返回数据之间的数据可能会发生一些事情。
Update:The main problem with the Auto/ManualResetEventis that it is too vulnerable. A thread could wait for the event, and then not give enough time for anyone else to get it before changing it to something else. Is there a way to use locks or something else? Maybe using get and set statements.
更新:的主要问题Auto/ManualResetEvent是它太脆弱了。线程可能会等待该事件,然后在将其更改为其他内容之前不会给其他任何人足够的时间来获取它。有没有办法使用锁或其他东西?也许使用 get 和 set 语句。
回答by Vinoth
You can use ManualResetEvent. Reset the event before you fire secondary thread and then use the WaitOne()method to block the current thread. You can then have secondary thread set the ManualResetEvent which would cause the main thread to continue. Something like this:
您可以使用 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
}
回答by mbarthelemy
A very easy kind of event you can wait for is the ManualResetEvent, and even better, the ManualResetEventSlim.
您可以等待的一种非常简单的事件是ManualResetEvent,甚至更好的是ManualResetEventSlim.
They have a WaitOne()method that does exactly that. You can wait forever, or set a timeout, or a "cancellation token" which is a way for you to decide to stop waiting for the event (if you want to cancel your work, or your app is asked to exit).
他们有一种WaitOne()方法可以做到这一点。你可以永远等待,或者设置一个超时,或者一个“取消令牌”,这是你决定停止等待事件的一种方式(如果你想取消你的工作,或者你的应用程序被要求退出)。
You fire them calling Set().
你解雇他们打电话Set()。
Hereis the doc.
这是文档。
回答by Enigmativity
If you're happy to use the Microsoft Reactive Extensions, then this can work nicely:
如果您乐于使用 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;
}
I can use it like this:
我可以这样使用它:
var foo = new Foo();
ThreadPoolScheduler.Instance
.Schedule(
TimeSpan.FromSeconds(5.0),
() => foo.SendLine("Bar!"));
var resp = foo.ReadLine();
Console.WriteLine(resp);
I needed to call the SendLinemessage on a different thread to avoid locking, but this code shows that it works as expected.
我需要SendLine在不同的线程上调用消息以避免锁定,但此代码显示它按预期工作。
回答by Adam
If the current method is async then you can use TaskCompletionSource. Create a field that the event handler and the current method can access.
如果当前方法是异步的,那么您可以使用 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);
}
This example uses a form that has a textblock named WelcomeTitle and two buttons. When the first button is clicked it starts the click event but stops at the await line. When the second button is clicked the task is completed and the WelcomeTitle text is updated. If you want to timeout as well then change
此示例使用具有名为 WelcomeTitle 的文本块和两个按钮的表单。当第一个按钮被点击时,它开始点击事件,但在等待行停止。单击第二个按钮时,任务完成并更新 WelcomeTitle 文本。如果您也想超时,请更改
await tcs.Task;
to
到
await Task.WhenAny(tcs.Task, Task.Delay(25000));
if (tcs.Task.IsCompleted)
WelcomeTitle.Text = "Task Completed";
else
WelcomeTitle.Text = "Task Timed Out";

