在WPF應用程序中,我有一個通過網絡接收消息的類。每當該類的對象收到完整的消息時,就會引發一個事件。在應用程序的MainWindow中,我有一個訂閱該事件的事件處理程序。事件處理程序保證在應用程序的GUI線程上調用。如何避免使用異步無效事件處理程序重入?
每當調用事件處理程序時,都需要將消息的內容應用於模型。這樣做可能會非常昂貴(在當前硬件上> 200ms)。這就是爲什麼使用Task.Run將消息應用於線程池的原因。
現在,可以非常接近地接收消息,因此可以調用事件處理程序,而前一個更改仍在處理中。確保消息只應用於一個最簡單的方法是什麼?到目前爲止,我已經想出了以下情況:
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));
}
這是一個明智的方式,以避免與異步無效的事件處理程序重入?
編輯:刪除OnMessageReceived
中不必要的await this.pending;
聲明。
編輯2:消息必須以接收它們的相同順序應用於模型。
@Servy:你的意思是在OnMessageReceived中?好問題,我想這不是必要的。 –
@Servy:我同意在OnMessageReceived中沒有必要,但它在ApplyToModel中,對吧? –
我明白你現在在做什麼。 – Servy