Trong một ứng dụng WPF, tôi có một lớp nhận tin nhắn qua mạng. Bất cứ khi nào một đối tượng của lớp học đã nhận được một tin nhắn đầy đủ, một sự kiện được nêu ra. Trong MainWindow của ứng dụng, tôi có một trình xử lý sự kiện đã đăng ký với sự kiện đó. Trình xử lý sự kiện được đảm bảo được gọi trên luồng GUI của ứng dụng.Làm thế nào để tránh reentrancy với bộ xử lý sự kiện không đồng bộ void?
Bất cứ khi nào trình xử lý sự kiện được gọi, nội dung của tin nhắn cần được áp dụng cho mô hình. Làm như vậy có thể khá tốn kém (> 200ms trên phần cứng hiện tại). Đó là lý do tại sao áp dụng tin nhắn được tải lên hồ bơi thread với Task.Run.
Bây giờ, các tin nhắn có thể được nhận liên tiếp rất gần, do đó, trình xử lý sự kiện có thể được gọi trong khi thay đổi trước đó vẫn đang được xử lý. Cách đơn giản nhất để đảm bảo rằng thư chỉ được áp dụng một lần vào thời điểm nào? Cho đến nay, tôi đã đi lên với những điều sau đây:
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.
}
}
Điều này dường như làm việc như mong đợi, tuy nhiên nó cũng xuất hiện này chắc chắn sẽ tạo ra một "rò rỉ bộ nhớ", bởi vì nhiệm vụ để áp dụng một thông báo sẽ luôn luôn đầu tiên đợi nhiệm vụ đã áp dụng tin nhắn trước đó. Nếu có, thì thay đổi sau đây nên tránh sự rò rỉ:
private async Task ApplyToModel(EventArgs e)
{
if (!this.pending.IsCompleted)
{
await this.pending;
}
await Task.Run(() => this.model.Apply(e));
}
Đây có phải là cách hợp lý để tránh sự reentrancy với trình xử lý sự kiện không đồng bộ không?
EDIT: Đã xóa câu hỏi không cần thiết await this.pending;
trong OnMessageReceived
.
CHỈNH SỬA 2: Thư phải được áp dụng cho mô hình theo cùng thứ tự mà chúng đã nhận được.
@Servy: Bạn có nghĩa là trong OnMessageReceived? Câu hỏi hay, tôi đoán nó không cần thiết. –
@Servy: Tôi đồng ý rằng không cần thiết trong OnMessageReceived, nhưng nó nằm trong ApplyToModel, phải không? –
Tôi thấy những gì bạn đang làm với nó ngay bây giờ. – Servy