Dưới đây là những điểm nổi bật:
- Bạn không thể thực hiện cuộc gọi điều khiển UI từ một thread khác nhau so với cái chúng được tạo ra trên (Chủ đề của mẫu).
- Yêu cầu ủy nhiệm (tức là móc sự kiện) được kích hoạt trên cùng một chuỗi với đối tượng đang kích hoạt sự kiện.
Vì vậy, nếu bạn có một chuỗi "động cơ" riêng biệt thực hiện một số công việc và xem một số giao diện người dùng. Cháy của động cơ là một sự kiện thay đổi đối tượng đã được nối bởi Biểu mẫu. Nhưng đại biểu gọi lại rằng Biểu mẫu đã đăng ký với công cụ được gọi trên luồng của động cơ… chứ không phải trên luồng của Biểu mẫu. Và do đó bạn không thể cập nhật bất kỳ điều khiển nào từ cuộc gọi lại đó. Doh!
BeginInvoke đến để giải cứu. Chỉ cần sử dụng mô hình mã hóa đơn giản này trong tất cả các phương thức gọi lại của bạn và bạn có thể chắc chắn rằng mọi thứ sẽ ổn thỏa:
private delegate void EventArgsDelegate(object sender, EventArgs ea);
void SomethingHappened(object sender, EventArgs ea)
{
//
// Make sure this callback is on the correct thread
//
if (this.InvokeRequired)
{
this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });
return;
}
//
// Do something with the event such as update a control
//
textBox1.Text = "Something happened";
}
Thực sự khá đơn giản.
- Sử dụng InvokeRequired để tìm hiểu xem cuộc gọi lại này có xảy ra đúng chuỗi không.
- Nếu không, sau đó reinvoke gọi lại trên các chủ đề chính xác với các thông số tương tự. Bạn có thể reinvoke một phương pháp bằng cách sử dụng các phương thức Gọi (chặn) hoặc BeginInvoke (không chặn).
- Lần sau khi hàm được gọi, InvokeRequired trả về false vì chúng tôi đang ở đúng chuỗi và mọi người đều hài lòng.
Đây là một cách rất nhỏ gọn để giải quyết vấn đề này và làm cho Biểu mẫu của bạn an toàn từ cuộc gọi lại nhiều sự kiện.
Nguồn
2008-08-08 17:35:40
Tôi thường thích BeginInvoke để Gọi, nhưng có một báo trước: người ta phải tránh xếp hàng quá nhiều sự kiện. Tôi sử dụng một biến updateRequired được thiết lập để 1 khi một BeginInvoke sẽ xảy ra, và chỉ thực hiện BeginInvoke nếu nó đã bằng không (sử dụng Interlocked.Exchange). Trình xử lý hiển thị có một vòng lặp while xóa updateRequired và, nếu nó không phải là 0, thực hiện cập nhật và các vòng lặp. Trong một số trường hợp, bộ hẹn giờ được thêm vào để giới hạn tần suất cập nhật thêm (để tránh việc sử dụng mã toàn bộ thời gian cập nhật dữ liệu tiến trình thay vì thực hiện công việc thực tế) nhưng điều đó phức tạp hơn. – supercat
@Supercat ... điều chỉnh sự kiện là một chủ đề quan trọng cho nhiều ứng dụng, nhưng nó không phải là một cái gì đó mà nên là một phần của lớp giao diện người dùng. Một bus proxy sự kiện riêng biệt sẽ được tạo để nhận, xếp hàng, kết hợp và gửi lại các sự kiện vào các khoảng thời gian thích hợp. Bất kỳ thuê bao nào vào bus sự kiện không nên biết rằng sự kiện điều chỉnh đang xảy ra. –
Tôi có thể thấy những nơi mà một "bus sự kiện" riêng biệt để xử lý đồng bộ hóa có thể hữu ích, nhưng trong nhiều trường hợp có vẻ dễ dàng nhất đối với người dùng cuối giống như lớp chỉ báo tiến trình nếu lớp chỉ đơn giản là tiếp xúc với thuộc tính MinimumUpdateInterval. – supercat