WPF アプリケーションには、ネットワーク経由でメッセージを受信するクラスがあります。上記のクラスのオブジェクトが完全なメッセージを受信するたびに、イベントが発生します。アプリケーションの MainWindow には、そのイベントにサブスクライブされたイベント ハンドラーがあります。イベント ハンドラーは、アプリケーションの GUI スレッドで呼び出されることが保証されています。
イベント ハンドラーが呼び出されるたびに、メッセージの内容をモデルに適用する必要があります。これを行うと、非常にコストがかかる可能性があります (現在のハードウェアでは 200 ミリ秒以上)。そのため、メッセージの適用は、Task.Run を使用してスレッド プールにオフロードされます。
現在、メッセージは非常に近い間隔で受信できるため、前の変更がまだ処理されている間にイベント ハンドラーを呼び出すことができます。メッセージが一度に 1 つだけ適用されるようにする最も簡単な方法は何ですか? これまでのところ、次のことを思いつきました。
using System;
using System.Threading.Tasks;
using System.Windows;
public partial class MainWindow : Window
{
private Model model = new Model();
private Task pending = Task.FromResult<bool>(false);
// Assume e carries a message received over the network.
private void OnMessageReceived(object sender, EventArgs e)
{
this.pending = ApplyToModel(e);
}
private async Task ApplyToModel(EventArgs e)
{
await this.pending;
await Task.Run(() => this.model.Apply(e)); // Assume this is an expensive call.
}
}
これは期待どおりに動作するように見えますが、メッセージを適用するタスクは常に、前のメッセージを適用したタスクを最初に待機するため、必然的に「メモリ リーク」が発生するようです。その場合、次の変更によりリークを回避できます。
private async Task ApplyToModel(EventArgs e)
{
if (!this.pending.IsCompleted)
{
await this.pending;
}
await Task.Run(() => this.model.Apply(e));
}
これは、async void イベント ハンドラーで再入可能性を回避する賢明な方法ですか?
編集: の不要なawait this.pending;
ステートメントを削除しましたOnMessageReceived
。
EDIT 2 : メッセージは、受信されたのと同じ順序でモデルに適用する必要があります。