【发布时间】:2017-07-28 17:13:16
【问题描述】:
我正在构建一个可供其他开发人员使用的控件,并且在一些地方我有开发人员可以订阅的事件来执行一些可能很长的操作。
我的目标是让他们选择使用 async/await、多线程、backgroundworkers 等。如果他们选择,但仍然能够在事件调用完成之前完成执行。这是一个伪代码示例(我意识到这不会编译,但希望意图很明确):
我的代码:
public event MyEventHandler MyEvent;
private void InvokeMyEvent()
{
var args = new MyEventArgs();
// Keep the UI responsive until this returns
await MyEvent?.Invoke(this, args);
// Then show the result
MessageBox.Show(args.Result);
}
开发者/订阅者的潜在代码:
// Option 1: The operation is very quick,
// so the dev doesn't mind doing it synchronously
private void myForm_MyEvent(object sender, MyEventArgs e)
{
// Short operation, I'll just do it right here.
string result = DoQuickOperation();
e.Result = result;
}
// Option 2, The operation is long,
// so the dev wants to do it on another thread and keep the UI responsive.
private void myForm_MyEvent(object sender, MyEventArgs e)
{
myForm.ShowProgressPanel();
// Long operation, I want to multithread this!
Task.Run(() =>
{
string result = DoVeryLongOperation();
e.Result = result;
})
.ContinueWith((task) =>
{
myForm.HideProgressPanel();
}
}
有没有标准的方法来完成这个?我正在查看委托的 BeginInvoke 和 EndInvoke 方法,希望它们能有所帮助,但我并没有真正想出任何东西。
任何帮助或建议将不胜感激!谢谢!
【问题讨论】:
-
每个订阅者都会有结果吗?
-
实际上只有一个订阅者,但如果有多个,我只关心最后一个。如果他们不设置它,结果就是 MyEventArgs 构造它的任何内容。
-
您需要进行两项更改:(1) 处理程序需要返回
Task,以便调用代码可以知道它们何时完成。 (2) 你不能使用语法糖来忽略订阅的处理程序的数量。?.Invoke不仅不起作用,因为它让您在没有处理程序时尝试等待null,多播也不起作用。 您需要调用GetInvocationList()并循环。 即使最后一个处理程序是获得确定args.Result值的特权的处理程序,您仍然需要等待返回的Task对象来自其他人。 -
1) 这不会破坏事件处理程序的约定吗?即使他们不使用任务,它也会迫使开发人员返回任务。 2)当然,有道理,但我需要他们一个接一个地执行并返回,这是无法做到的。
-
1) 太糟糕了,您要求的东西在不违反约定的情况下是不可能的。 2)我只是告诉你怎么做,所以不要告诉我它做不到。在获得第二个之前是否等待第一个
Task取决于您...但是如果您让异步处理程序并行运行,您将无法控制对args.Result的更改发生的顺序。
标签: c# multithreading events asynchronous