4

C#コンソールアプリケーションのコンテキストで、メッセージを非同期に受信するために使用されるループを作成すると、受信したメッセージごとに次のようなイベントが発生します。

while (true)
{
   var message = await ReceiveMessageAsync();
   ReceivedMessage(new ReceivedMessageEventArgs(message));
}

ここで、イベントに複数のサブスクライバーがある場合(たとえば、例として3つのサブスクライバーがある場合)、それらすべてが次のような非同期イベントハンドラーを使用します。

async void OnReceivedMessageAsync(object sender, ReceivedMessageEventArgs args)
{
   await TreatMessageAsync(args.Message);
}

メッセージオブジェクトはスレッドセーフな方法でコーディングする必要がありますか?異なるイベントハンドラーからのTreatMessageAsyncコードがすべてのサブスクライバーに対して同時に実行される可能性があるため(イベントが発生すると、サブスクライバーの3つの非同期イベントハンドラーが呼び出され、それぞれが異なるスレッドで同時に実行される可能性のある非同期操作を起動すると思いますタスクスケジューラによる)。それとも私は間違っていますか?

ありがとう !

4

2 に答える 2

2

スレッドセーフな方法でコーディングする必要があります。これを行う最も簡単な方法は、不変にすることです。

真のイベントがある場合、その引数は不変である必要があります。真のイベントではないもの(コマンド実装など)にイベントハンドラーを使用している場合は、APIを変更することをお勧めします。

各ハンドラーは順番に開始されるため、イベントハンドラーを同時に実行することは可能ですが、同時に再開することもできます。

于 2012-11-30T03:02:42.237 に答える
1

Stephenが提案したように、この場合にスレッドセーフを実現する最も簡単な方法は、不変のイベント引数を使用することです。

ほとんどの場合、argsでさえ、オブザーバブル側からオブザーバーへ(つまり、イベントサブスクライバーからイベント所有者へ)変更する必要なしにオブザーバーに通知するためにのみ使用されます。Chains of Responsibilityデザインパターンの実装などの特殊なケースでは、イベント引数は変更可能である必要がありますが、それ以外の場合は変更できません。

この場合の不変性は、並行ハンドラーを簡単に実装するだけでなく、APIを誤って使用できるようになったため、より明確な設計と保守性の向上につながります。

結論は次のとおりです。メソッドをスレッドセーフに実装する必要がありますが、イベントが非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();
    }
}
于 2012-11-29T22:12:48.050 に答える