在 c # 中触发事件之前如何阻塞

在询问 这个问题之后,我想知道是否有可能等待触发事件,然后获取事件数据并返回部分数据。就像这样:

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

请确保您提供的任何解决方案直接返回值,而不是从其他方面获取值。我想知道上面的方法是否可行。我知道 Auto/ManuelResetEvent,但我不知道它们是否像上面那样直接返回值。

更新: 我使用 MyEventHandler(其中包含一个 Message字段)声明了一个事件。我在另一个线程中有一个名为 ReadLine的方法,它正在等待事件激发。当事件触发时,WaitForValue 方法(事件处理场景的一部分)返回包含消息的事件参数。然后,ReadLine 将消息返回给所调用的内容。

对于 这个问题,我问的答案是我做了什么,但是感觉不太对。在 ManuelResetEvent 触发和程序检索数据并返回数据之间,似乎有什么事情可能发生。

更新: Auto/ManualResetEvent的主要问题是它太脆弱了。线程可以等待事件,然后在将其更改为其他内容之前不给其他任何人足够的时间来获取它。有没有办法用锁或者别的什么?可以使用 get 和 set 语句。

121642 次浏览

你可以使用 手动重置事件。在激发辅助线程之前重置事件,然后使用 等一下方法阻止当前线程。然后可以让辅助线程设置 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
}

一个非常简单的事件,你可以等待的是 ManualResetEvent,甚至更好的,ManualResetEventSlim

他们有一个 WaitOne()方法可以做到这一点。你可以永远等待,或设置一个超时,或一个“取消令牌”,这是一种方式,让你决定停止等待事件(如果你想取消你的工作,或你的应用程序被要求退出)。

你解雇他们打电话 Set()

这里 是文档。

如果你乐意使用微软的反应扩展,那么这个可以很好地工作:

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消息以避免锁定,但是这段代码表明它按预期工作。

如果当前方法是异步的,则可以使用 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);
}

此示例使用具有名为 WelcomTitle 的文本块和两个按钮的表单。当单击第一个按钮时,它启动单击事件,但在等待线处停止。当单击第二个按钮时,任务完成,并更新 WelcomTitle 文本。如果你也想超时,那么改变

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";