【发布时间】:2016-02-10 23:16:09
【问题描述】:
有时我们希望事件发布者在触发后的操作取决于事件处理程序设置的某些状态。一个常见的例子是接受CancelEventArgs 的事件。事件发布者在触发后采取行动,条件是 Cancel 属性的值。如果处理程序长时间运行,我们不想阻塞 UI 线程(用户可以在等待时做其他事情)。但是异步处理程序可能会在状态设置之前返回,并且发布者在需要时不会有正确的值。我通过简单地在 event 参数中提供一个可变的 Task 属性来解决这个问题。处理程序负责设置其值,而发布者在触发后等待它。这似乎工作正常。
一个反对意见可能是有状态的事件参数可以说是不好的做法,除非你假设只有一个处理程序。您可以使用高阶函数而不是事件,这将通过强制执行一个“处理程序”来处理上述异议。
我不确定的一件事是 async/await 如何处理多个等待者。
- 是否可以保证继续执行的顺序?
- 是否会出现竞争条件,其中触发的其余部分可能在事件处理程序的其余部分之前执行?
答案是yes。这感觉这对于嵌套的异步方法来说通常是一个问题,只要调用者和被调用者在等待之后都有动作。
- 我的工作方式有什么我没有提到的缺点吗?
- 是否有其他一些众所周知或广为接受的最佳实践来实现我不知道的目标?
- 关于如何处理竞争条件的任何想法?
谢谢!
class Publisher
{
void RaiseMyEvent()
{
var e = new MyEventArgs();
OnRaiseMyEvent(e);
if (e.Task != null) await e.HandlerTask;
if (e.Cancel)
{
// Do one thing
}
else
{
// Do the other
}
}
}
class Subscriber
{
void MyEventHandler(object sender, CancelEventArgs e)
{
// Notify user to wait on process
e.Task = SomeAsyncMethod();
await e.Task;
e.Cancel = GetOutcome();
// Clear any notification
}
bool GetOutcome() { }
}
实际上,我们可以通过确保触发方法所需的事件参数中的任何所需状态值在处理程序中的继续之前设置来避免竞争:
class Subscriber
{
void MyEventHandler(object sender, CancelEventArgs e)
{
// Notify user to wait on process
e.Task = Task.Run(() =>
{
//Do stuff
e.Cancel = GetOutcome();
}
await e.Task;
// Clear any notification
}
bool GetOutcome() { }
}
两个延续都在 UI 线程上执行,但我们不关心顺序。
【问题讨论】:
标签: c# events asynchronous