我想将(相对)较慢的事件处理程序转换为async
,以避免阻塞其他事件订阅者。在下面的示例中,我不希望Message
事件的其他订阅者必须等待。
我唯一想到的就是“作弊”:
client.Message += async (object sender, MessageEventArgs e) => {
await Task.Run(() => { });
logger.Info("Some info {0}", e.Message);
}
我当然可以将整个logger.Info
调用包装在Task
中,但是我不明白为什么这样会更好。我只是看起来不太像作弊。
我唯一的另一种想法是应该使处理程序保持同步,并且Message
事件仅应在延迟不重要的情况下进行预订。因此,我可能会有第二个事件,该事件仅由低延迟async
订阅者使用。
我正在这里寻求有关正确编码设计的反馈。
答案 0 :(得分:1)
如果要附加将在调用同一事件的所有同步处理程序之后将被调用的异步处理程序,并且还希望异步调用在当前同步上下文中发生,则可以使用Task.Yield
。对于GUI应用程序,这意味着您希望处理程序在UI线程上运行。
client.Message += async (sender, e) =>
{
await Task.Yield();
logger.Info("Some info {0}", e.Message);
}
请勿使用await Task.Run(() => { })
,因为它只是await Task.Yield()
的冗长,惯用,效率较低且(可能)不够健壮的替代方案。
如果要附加将在ThreadPool
线程(而不是当前同步上下文)上运行的异步处理程序,请使用Task.Run
:
client.Message += async (sender, e) =>
{
await Task.Run(() => logger.Info("Some info {0}", e.Message));
}
只有在确定logger
对象是线程安全的,支持并发操作并且不需要线程亲和性(就像许多COM组件一样)的情况下,才应该这样做。