Tôi có nên tránh các trình xử lý sự kiện 'async void' không?


118

Tôi biết thường được coi là một ý tưởng tồi khi sử dụng async voidcác phương thức fire-and-forget để bắt đầu các tác vụ, bởi vì không có dấu vết của nhiệm vụ đang chờ xử lý và rất khó để xử lý các ngoại lệ có thể nằm trong một phương thức như vậy.

Nói chung, tôi cũng nên tránh các async voidtrình xử lý sự kiện? Ví dụ,

private async void Form_Load(object sender, System.EventArgs e)
{
        await Task.Delay(2000); // do async work
        // ...
} 

Tôi có thể viết lại nó như thế này:

Task onFormLoadTask = null; // track the task, can implement cancellation

private void Form_Load(object sender, System.EventArgs e)
{
        this.onFormLoadTask = OnFormLoadTaskAsync(sender, e);
} 

private async Task OnFormLoadTaskAsync(object sender, System.EventArgs e)
{
        await Task.Delay(2000); // do async work
        // ...
} 

Đá dưới nước dành cho người xử lý sự kiện không đồng bộ là gì, ngoài khả năng tái thích hợp?


Bạn nên nhưng bạn không thể. Bên cạnh đó, tất cả những lưu ý bạn phải thực hiện khi sử dụng async void đều đã được yêu cầu bởi các trình xử lý sự kiện UI.
Paulo Morgado

Và lần truy cập lại xảy ra do các hoạt động không đồng bộ được kích hoạt bởi trình xử lý sự kiện chứ không phải do việc sử dụng async-await của chính nó.
Paulo Morgado

Câu trả lời:


152

Nguyên tắc là tránh async void ngoại trừ khi được sử dụng trong trình xử lý sự kiện, do đó, sử dụng async voidtrong trình xử lý sự kiện là OK.

Điều đó nói rằng, vì lý do thử nghiệm đơn vị, tôi thường thích nêu ra logic của tất cả các async voidphương pháp. Ví dụ,

public async Task OnFormLoadAsync(object sender, EventArgs e)
{
  await Task.Delay(2000);
  ...
}

private async void Form_Load(object sender, EventArgs e)
{
  await OnFormLoadAsync(sender, e);
}

Tôi tò mò ... có lý do gì mà bạn không thay đổi Form_Loadquyền truy cập của public? Có vẻ như mã sẽ ít dài hơn theo cách đó.
InteXX

Rất tiếc, đừng bận tâm ... VBer đang cố đọc C # ở đây ... Tôi chỉ nhận thấy kiểu trả về OnFormLoadAsync. Bây giờ tôi thấy rằng điều này làm cho một thủ thuật hữu ích. Cảm ơn.
InteXX

Tất cả những gì đã nói, bạn có thể xem và đưa ra ý kiến ở đây . Cảm ơn!
InteXX

2
@ AlexHopeO'Connor: HandledCờ phải được đặt đồng bộ; nó không thể được sử dụng asyncđể đưa ra quyết định xem sự kiện có được xử lý hay không.
Stephen Cleary

1
@ AlexHopeO'Connor: Đã lâu rồi tôi không làm việc với ứng dụng WPF, nhưng trước đây tôi đã sử dụng các giải pháp tương tự như vậy. Tức là làm cho ICommand.Executephương pháp async void; Tôi xem xét việc này có thể chấp nhận kể từ khi ICommand.Executemột cách logic xử lý sự kiện.
Stephen Cleary

49

Nói chung, tôi cũng nên tránh các trình xử lý sự kiện void async?

Nói chung, các trình xử lý sự kiện là một trường hợp mà phương thức void async không phải là một mùi mã tiềm năng.

Bây giờ, nếu bạn cần theo dõi nhiệm vụ vì lý do nào đó thì kỹ thuật bạn mô tả là hoàn toàn hợp lý.


6

Có, nói chung là trường hợp duy nhất có khoảng trống không đồng bộ của trình xử lý sự kiện. Nếu bạn muốn biết thêm về nó, bạn có thể xem một video tuyệt vời ở đây tại kênh 9

The only case where this kind of fire-and-forget is appropriate is in top-level event-handlers. Every other async method in your code should return "async Task".

đây là liên kết


' Trình xử lý sự kiện cấp cao nhất ' là một gợi ý quan trọng. Khi sử dụng trình xử lý sự kiện async void trên trình xử lý sự kiện cấp thấp hơn, nó có thể gây ra vấn đề lớn với các ngoại lệ không bị bắt.
Portikus

Cảm ơn vì liên kết video, rất hữu ích
lsp

5

Nếu bạn sử dụng ReSharper, một Tiện ích mở rộng được đề xuất miễn phí có thể hữu ích cho bạn. Nó phân tích các phương thức "async void" và làm nổi bật khi được sử dụng không phù hợp. Tiện ích mở rộng có thể phân biệt các cách sử dụng khác nhau của tình trạng vô hiệu hóa không đồng bộ và cung cấp các bản sửa lỗi nhanh chóng phù hợp được mô tả tại đây: wiki Khuyến nghị-Tiện ích mở rộng .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.