Thư viện tác vụ async của tôi có nên nuốt ngoại lệ một cách lặng lẽ không?


10

Tôi mới biết rằng .NET 4.5 đã giới thiệu một thay đổi về cách Taskxử lý các ngoại lệ bên trong . Cụ thể, họ lặng lẽ bị đàn áp.

Lý do chính thức cho lý do tại sao điều này được thực hiện dường như là "chúng tôi muốn thân thiện hơn với các nhà phát triển thiếu kinh nghiệm":

Trong .NET 4.5, Nhiệm vụ có sự nổi bật hơn đáng kể so với .NET 4, khi chúng được đưa vào ngôn ngữ C # và Visual Basic như một phần của các tính năng async mới được các ngôn ngữ hỗ trợ. Điều này có hiệu lực di chuyển Nhiệm vụ ra khỏi miền của các nhà phát triển có kinh nghiệm vào vương quốc của mọi người. Kết quả là, nó cũng dẫn đến một loạt các sự đánh đổi mới về mức độ nghiêm ngặt của việc xử lý ngoại lệ.

( nguồn )

Tôi đã học cách tin tưởng rằng nhiều quyết định trong .NET được đưa ra bởi những người thực sự biết họ đang làm gì và thường có một lý do rất chính đáng đằng sau những điều họ quyết định. Nhưng cái này thoát khỏi tôi.

Nếu tôi đang thiết kế thư viện tác vụ không đồng bộ của riêng mình, thì lợi thế của việc nuốt các ngoại lệ mà các nhà phát triển của Khung nhìn thấy là tôi không thấy là gì?


4
+1. Câu hỏi hay, với thực tế là không truyền bá ngoại lệ mà bạn không thể xử lý là một thực tiễn tồi ngay cả khi bối cảnh không đồng bộ. Ngoài ra, tranh luận về các nhà phát triển thiếu kinh nghiệm là khá khập khiễng, IMHO. Điều gì sẽ gây khó xử cho người mới bắt đầu hơn là một đoạn mã không những không hoạt động mà còn không có ngoại lệ nào?
Arseni Mourzenko

@MainMa Chính xác là suy nghĩ của tôi, nhưng tôi đã sai trước đây (trên thực tế, họ đã có một lý do rất tốt nhưng không rõ ràng), vì vậy tôi nghĩ tôi đã hỏi.
Roman Starkov

2
Ồ, tôi rất vui vì bạn đã đăng bài này chỉ vì đây là lần đầu tiên tôi nghe về nó; và nó chắc chắn vi phạm Pola . Bạn đúng rằng những kẻ đó thực sự biết những gì họ đang làm, vì vậy tôi thực sự tò mò lý do nào có thể đằng sau điều này ... Tự hỏi liệu nó có lý do kỹ thuật hơn dựa trên việc triển khai Async và cách thức ngoại lệ sẽ lan truyền khi đáy được chuyển đến một coroutine ..
Jimmy Hoffa

Câu trả lời:


2

Đối với giá trị của nó, tài liệu bạn liên kết để đưa ra một trường hợp ví dụ là biện minh:

Task op1 = FooAsync(); 
Task op2 = BarAsync(); 
await op1; 
await op2;

Trong mã này, nhà phát triển sẽ khởi chạy hai hoạt động không đồng bộ để chạy song song và sau đó chờ đợi không đồng bộ cho từng sử dụng tính năng ngôn ngữ chờ đợi mới ... [C] onsider điều gì sẽ xảy ra nếu cả hai lỗi op1 và op2. Chờ đợi op1 sẽ tuyên truyền ngoại lệ của op1, và do đó op2 sẽ không bao giờ được chờ đợi. Do đó, ngoại lệ của op2 sẽ không được quan sát và quá trình cuối cùng sẽ sụp đổ.

Để giúp các nhà phát triển dễ dàng viết mã không đồng bộ dựa trên Nhiệm vụ, .NET 4.5 thay đổi hành vi ngoại lệ mặc định cho các ngoại lệ không quan sát được. Mặc dù các ngoại lệ không được quan sát sẽ vẫn khiến sự kiện UnobservedTaskException được nêu ra (không làm như vậy sẽ là một thay đổi vi phạm), quá trình sẽ không bị sập theo mặc định. Thay vào đó, ngoại lệ sẽ bị ăn sau khi sự kiện được nêu ra, bất kể người xử lý sự kiện có quan sát ngoại lệ hay không.

Tôi không bị thuyết phục bởi điều này. Nó loại bỏ khả năng xảy ra lỗi rõ ràng nhưng khó theo dõi (sự cố chương trình bí ẩn có thể xảy ra lâu sau lỗi thực tế), nhưng thay thế nó bằng khả năng xảy ra lỗi hoàn toàn im lặng - có thể trở thành vấn đề khó theo dõi sau này trong chương trình của bạn. Đó dường như là một lựa chọn đáng ngờ với tôi.

Hành vi có thể định cấu hình - nhưng tất nhiên, 99% các nhà phát triển sẽ chỉ sử dụng hành vi mặc định, không bao giờ nghĩ về vấn đề này. Vì vậy, những gì họ chọn làm mặc định là một vấn đề lớn.


Nhưng bạn làm được một ngoại lệ bình thường đối với op1, phải không? Nếu cái đó vẫn chưa được xử lý, nó sẽ làm giảm quá trình, phải không?
Timwi

1
@Timwi, theo tôi hiểu, không op1ngoại lệ hay op2ngoại lệ nào sẽ làm giảm chương trình. Ưu điểm là bạn có cơ hội quan sát cả hai. Nhưng nếu bạn không, cả hai sẽ bị nuốt chửng. Tôi có thể sai mặc dù.

Tôi thực sự ngạc nhiên khi họ thấy nó rất quan trọng khi để bạn "quan sát" cả hai. Nếu bạn muốn "quan sát" chúng, bạn bắt chúng và báo cáo sự xuất hiện của chúng thông qua kết quả nhiệm vụ! ... Đồng ý với lý luận của bạn, +1
Roman Starkov

2

TL; DR - Không , bạn không nên bỏ qua các ngoại lệ một cách lặng lẽ.


Hãy xem xét các giả định.

  • Niềm tin mù quáng của bạn

Tôi đã học cách tin tưởng rằng nhiều quyết định trong .NET được đưa ra bởi những người thực sự biết họ đang làm gì và thường có một lý do rất chính đáng đằng sau những điều họ quyết định. Nhưng cái này thoát khỏi tôi.

MS có một số người thực sự xuất sắc trong đội ngũ nhân viên, không có nghi ngờ. Họ cũng có một số nhà quản lý và giám đốc điều hành ... không biết gì và luôn đưa ra những quyết định tồi tệ. Nhiều như chúng tôi muốn tin vào câu chuyện cổ tích của nhà khoa học am hiểu kỹ thuật thúc đẩy sự phát triển sản phẩm, thực tế còn nghiệt ngã hơn nhiều.

Quan điểm của tôi, nếu bản năng của bạn nói với bạn điều gì đó có thể sai thì có một cơ hội tốt (và tiền lệ trong quá khứ) rằng nó sai.

  • Đây là một vấn đề kỹ thuật

Làm thế nào để xử lý các trường hợp ngoại lệ trong trường hợp này là một quyết định yếu tố con người, không phải là một quyết định kỹ thuật. Một cách lỏng lẻo, một ngoại lệ là một lỗi. Trình bày lỗi, ngay cả khi được định dạng kém, cung cấp thông tin quan trọng cho người dùng cuối. Cụ thể, một cái gì đó đã đi sai.

Hãy xem xét cuộc trò chuyện giả thuyết này giữa người dùng cuối và nhà phát triển.

Người dùng: Ứng dụng bị hỏng.
Dev: Cái gì đã hỏng?
Thành viên: Tôi không biết, tôi bấm vào nút và không có gì xảy ra.
Dev: Ý bạn là gì khi không có gì xảy ra?
Người dùng: Nhìn, tôi bấm, tôi chờ, và tôi đợi, và tôi chờ, và không có gì ... rõ ràng là nó đã bị hỏng.

Nhà phát triển giả thuyết nghèo nàn của chúng tôi bây giờ phải tìm ra điều gì đã xảy ra trong chuỗi sự kiện. Nó ở đâu?
Thông báo xử lý sự kiện -> Thường xuyên để xử lý sự kiện -> Phương thức được kích hoạt bởi trình xử lý -> bắt đầu cuộc gọi không đồng bộ -> 7 lớp của mạng OSI -> truyền vật lý -> sao lưu 7 lớp của mạng OSI -> dịch vụ nhận -> phương thức được gọi bởi dịch vụ -> ... -> trả lời trả lời được gửi bởi dịch vụ -> .... -> nhận asynch -> xử lý trả lời asynch -> ...

Và xin lưu ý rằng tôi đã theo dõi một số đường dẫn lỗi tiềm ẩn ở đó.


Sau khi giải quyết một số giả định cơ bản, tôi nghĩ nó trở nên rõ ràng hơn tại sao lặng lẽ ngăn chặn các ngoại lệ là một ý tưởng tồi. Một thông báo lỗi ngoại lệ và liên quan là một chỉ báo quan trọng cho người dùng rằng đã xảy ra sự cố. Ngay cả khi tin nhắn là vô nghĩa đối với người dùng cuối, thông tin được nhúng có thể hữu ích cho Nhà phát triển để hiểu những gì đã sai. Nếu họ may mắn, thông điệp thậm chí sẽ dẫn đến giải pháp.

Tôi nghĩ một phần của vấn đề ở đây là một ngoại lệ được xử lý không chính xác sẽ làm hỏng ứng dụng của bạn. Bằng cách loại bỏ các ngoại lệ, ứng dụng sẽ không gặp sự cố. Có một số giá trị cho điều này mặc dù quan điểm không đồng bộ là cho phép ứng dụng tiếp tục hoạt động trong khi dữ liệu đang được truy xuất. Đó không phải là một phần mở rộng hợp lý để nói rằng nếu bạn có thể tiếp tục vận hành trong khi chờ kết quả thì lỗi truy xuất cũng không làm hỏng ứng dụng - cuộc gọi async được tuyên bố là không đủ quan trọng để kết thúc ứng dụng.

Vì vậy, nó là một giải pháp, nhưng nó giải quyết vấn đề sai. Sẽ không mất nhiều công sức để cung cấp trình bao bọc cho việc xử lý ngoại lệ để ứng dụng có thể tiếp tục chạy. Ngoại lệ bị bắt, thông báo lỗi được ném lên màn hình hoặc đăng nhập và ứng dụng được phép tiếp tục chạy. Việc loại bỏ ngoại lệ ngụ ý rằng bỏ qua lỗi sẽ là A-OK và thử nghiệm của bạn sẽ đảm bảo các ngoại lệ không bao giờ thoát ra khỏi trường.

Vì vậy, đánh giá cuối cùng của tôi là đây là một nỗ lực nửa vời trong việc giải quyết vấn đề được tạo ra bằng cách đẩy mọi người vào một mô hình không đồng bộ khi họ không sẵn sàng chuyển suy nghĩ của mình sang phương pháp đó.

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.