[英]Async event handlers and concurency
在C#控制台應用程序的上下文中,如果我創建一個用於異步接收消息的循環,它會為收到的每條消息引發一個事件,例如:
while (true)
{
var message = await ReceiveMessageAsync();
ReceivedMessage(new ReceivedMessageEventArgs(message));
}
現在,如果我有多個訂閱者(為了示例,讓我們說3個訂閱者),所有這些訂閱者都使用異步事件處理程序,例如:
async void OnReceivedMessageAsync(object sender, ReceivedMessageEventArgs args)
{
await TreatMessageAsync(args.Message);
}
消息對象應該以線程安全的方式編碼嗎? 我認為是這樣,因為來自不同事件處理程序的TreatMessageAsync代碼可以為所有訂閱者運行得很清楚(當引發事件時,調用訂閱者的三個異步事件處理程序,每個都啟動異步操作,這可能可以在不同的線程上運行由任務調度程序)。 還是我錯了?
謝謝 !
您應該以線程安全的方式對其進行編碼。 最簡單的方法是使其不可變。
如果你有一個真實的事件 ,那么它的參數應該是不可變的。 如果您使用的事件處理程序不是真正的事件 (如命令或實現 ),那么您可能想要修改您的API。
可以使用並發事件處理程序,因為每個處理程序將按順序啟動 ,但它們可以同時恢復 。
正如斯蒂芬所說,在這種情況下實現線程安全的最簡單方法是使用不可變事件args。
在大多數情況下,甚至args僅用於通知觀察者,而不需要從可觀察側到觀察者(即從事件訂閱者到事件所有者)的更改。 在某些特殊情況下,例如實現責任鏈設計模式事件args應該是可變的,但在所有其他情況下它們不應該是可變的。
在這種情況下,不變性不僅可以幫助您輕松實現並發處理程序,還可以實現更清晰的設計並提高可維護性,因為現在您可以錯誤地使用API。
結論是 :你應該實現你的方法thread-safety,但你應該知道,如果你的事件將從非UI線程觸發,那么將吞下來自事件處理程序的未處理異常。
這是使用異步void方法的危險:如果此方法將因異常而失敗,並且您將在沒有同步上下文的環境中調用它(例如來自控制台應用程序的線程池線程),那么您將獲得應用程序的域未處理異常將關閉:
internal class Program
{
static Task Boo()
{
return Task.Run(() =>
{
throw new Exception("111");
});
}
private static async void Foo()
{
await Boo();
}
static void Main(string[] args)
{
// Application will blow with DomainUnhandled excpeption!
try
{
Foo();
}
catch (Exception e)
{
// Will not catch it here!
Console.WriteLine(e);
}
Console.ReadLine();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.