In einer WPF-Anwendung habe ich eine Klasse, die Nachrichten über das Netzwerk empfängt. Immer wenn ein Objekt der Klasse eine vollständige Nachricht erhalten hat, wird ein Ereignis ausgelöst. Im MainWindow der Anwendung habe ich einen Event-Handler, der dieses Event abonniert hat. Der Event-Handler wird garantiert auf dem GUI-Thread der Anwendung aufgerufen.Wie vermeidet man die erneute Teilnahme mit asynchronen void Event-Handlern?
Wenn der Event-Handler aufgerufen wird, muss der Inhalt der Nachricht auf das Modell angewendet werden. Dies kann sehr kostspielig sein (> 200ms bei aktueller Hardware). Aus diesem Grund wird die Nachricht mit Task.Run auf den Thread-Pool ausgelagert.
Nun können Nachrichten in sehr kurzer Folge empfangen werden, sodass der Ereignishandler aufgerufen werden kann, während eine vorherige Änderung noch verarbeitet wird. Was ist der einfachste Weg, um sicherzustellen, dass Nachrichten nur einmal angewendet werden? Bisher habe ich mit folgendem kommen:
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.
}
}
Dies scheint wie erwartet zu funktionieren, aber es scheint auch dies wird unweigerlich ein „Gedächtnis Leck“ erzeugen, weil die Aufgabe eine Nachricht an gelten immer zuerst Warten Sie auf die Aufgabe, die die vorherige Nachricht angewendet hat. Wenn ja, dann sollte die folgende Änderung das Leck vermeiden:
private async Task ApplyToModel(EventArgs e)
{
if (!this.pending.IsCompleted)
{
await this.pending;
}
await Task.Run(() => this.model.Apply(e));
}
Ist die eine sinnvolle Art und Weise reentrancy mit Asynchron Leeren Event-Handler zu vermeiden?
EDIT: Die unnötige Anweisung in OnMessageReceived
entfernt.
EDIT 2: Die Nachrichten müssen in derselben Reihenfolge auf das Modell angewendet werden, in der sie empfangen wurden.
@Servy: Sie meinen in OnMessageReceived? Gute Frage, ich denke, es ist nicht notwendig. –
@Servy: Ich stimme zu, es ist nicht notwendig in OnMessageReceived, aber es ist in ApplyToModel, richtig? –
Ich sehe was du damit machst. – Servy