簡體   English   中英

異步事件處理程序和concurency

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM