In un'applicazione WPF, ho una classe che riceve i messaggi sulla rete. Ogni volta che un oggetto di detta classe ha ricevuto un messaggio completo, viene generato un evento. Nel MainWindow dell'applicazione ho un gestore di eventi sottoscritto a quell'evento. Il gestore di eventi è garantito per essere chiamato sul thread GUI dell'applicazione.Come evitare la rientranza con i gestori di eventi asincroni vuoti?
Ogni volta che viene chiamato il gestore eventi, il contenuto del messaggio deve essere applicato al modello. Fare ciò può essere piuttosto costoso (> 200 ms sull'hardware attuale). Ecco perché l'applicazione del messaggio viene scaricata nel pool di thread con Task.Run.
Ora i messaggi possono essere ricevuti in successione molto ravvicinata, quindi il gestore eventi può essere chiamato mentre una modifica precedente è ancora in elaborazione. Qual è il modo più semplice per garantire che i messaggi vengano applicati solo uno alla volta? Finora, mi è venuta in mente la seguente:
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.
}
}
Questo sembra funzionare come previsto, tuttavia, appare anche questo produrrà inevitabilmente una "perdita di memoria", perché il compito di applicare un messaggio sarò sempre prima attendere l'attività che ha applicato il messaggio precedente. Se è così, la seguente modifica dovrebbe evitare la perdita:
private async Task ApplyToModel(EventArgs e)
{
if (!this.pending.IsCompleted)
{
await this.pending;
}
await Task.Run(() => this.model.Apply(e));
}
E 'questo un modo ragionevole per evitare reentrancy con i gestori di eventi vuoto asincrone?
EDIT: Rimossa l'inutile dichiarazione await this.pending;
in OnMessageReceived
.
MODIFICA 2: i messaggi devono essere applicati al modello nello stesso ordine in cui sono stati ricevuti.
@Servy: Vuoi dire in OnMessageReceived? Bella domanda, immagino che non sia necessario. –
@Servy: Sono d'accordo che non è necessario in OnMessageReceived, ma è in ApplyToModel, giusto? –
Vedo cosa stai facendo ora. – Servy