Bạn đã hỏi một số câu hỏi trong câu hỏi của bạn. Tôi sẽ phá vỡ chúng một chút khác nhau so với bạn đã làm. Nhưng trước tiên hãy để tôi trả lời trực tiếp câu hỏi.
Tất cả chúng ta đều muốn một chiếc máy ảnh nhẹ, chất lượng cao và giá rẻ, nhưng giống như câu nói, bạn chỉ có thể nhận được tối đa hai trong số ba. Bạn đang ở trong tình trạng tương tự ở đây. Bạn muốn một giải pháp hiệu quả, an toàn và chia sẻ mã giữa các đường dẫn đồng bộ và không đồng bộ. Bạn sẽ chỉ nhận được hai trong số đó.
Hãy để tôi phá vỡ tại sao đó là. Chúng ta sẽ bắt đầu với câu hỏi này:
Tôi thấy rằng mọi người nói rằng bạn có thể sử dụng GetAwaiter().GetResult()
phương thức async và gọi nó từ phương thức đồng bộ hóa của bạn? Là chủ đề đó an toàn trong tất cả các kịch bản?
Điểm của câu hỏi này là "tôi có thể chia sẻ các đường dẫn đồng bộ và không đồng bộ bằng cách thực hiện đường dẫn đồng bộ chỉ đơn giản là chờ đồng bộ trên phiên bản không đồng bộ không?"
Hãy để tôi siêu rõ về điểm này bởi vì nó rất quan trọng:
BẠN NÊN NGAY LẬP TỨC DỪNG LẠI BẤT CỨ LỜI KHUYÊN NÀO TỪ MỌI NGƯỜI .
Đó là lời khuyên cực kỳ tồi tệ. Sẽ rất nguy hiểm khi tìm nạp đồng bộ kết quả từ một tác vụ không đồng bộ trừ khi bạn có bằng chứng cho thấy nhiệm vụ đã hoàn thành bình thường hoặc bất thường .
Lý do đây là lời khuyên cực kỳ tồi tệ, tốt, hãy xem xét kịch bản này. Bạn muốn cắt cỏ, nhưng lưỡi cắt cỏ của bạn bị hỏng. Bạn quyết định làm theo quy trình này:
- Đặt một lưỡi dao mới từ một trang web. Đây là một hoạt động không đồng bộ, độ trễ cao.
- Chờ đợi đồng bộ - nghĩa là ngủ cho đến khi bạn có lưỡi dao trong tay .
- Định kỳ kiểm tra hộp thư để xem lưỡi dao đã đến chưa.
- Lấy lưỡi dao ra khỏi hộp. Bây giờ bạn có nó trong tay.
- Lắp lưỡi cắt vào máy cắt.
- Cắt cỏ.
Chuyện gì xảy ra Bạn ngủ mãi mãi vì hoạt động kiểm tra thư hiện được kiểm soát trên một cái gì đó xảy ra sau khi thư đến .
Rất dễ gặp phải tình huống này khi bạn chờ đợi một cách đồng bộ một nhiệm vụ tùy ý. Nhiệm vụ đó có thể có công việc được lên lịch trong tương lai của chuỗi hiện đang chờ và bây giờ tương lai đó sẽ không bao giờ đến vì bạn đang chờ nó.
Nếu bạn chờ đợi không đồng bộ thì mọi thứ đều ổn! Bạn định kỳ kiểm tra thư và trong khi chờ đợi, bạn làm bánh sandwich hoặc đóng thuế hoặc bất cứ thứ gì; bạn tiếp tục hoàn thành công việc trong khi chờ đợi
Không bao giờ chờ đợi đồng bộ. Nếu nhiệm vụ được thực hiện, nó là không cần thiết . Nếu tác vụ không được thực hiện nhưng được lên lịch để chạy luồng hiện tại, thì nó không hiệu quả vì luồng hiện tại có thể phục vụ công việc khác thay vì chờ đợi. Nếu tác vụ không được thực hiện và lên lịch chạy trên luồng hiện tại, nó sẽ bị treo để chờ đồng bộ. Không có lý do chính đáng để chờ đợi đồng bộ, một lần nữa, trừ khi bạn đã biết rằng nhiệm vụ đã hoàn thành .
Để đọc thêm về chủ đề này, xem
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Stephen giải thích kịch bản trong thế giới thực tốt hơn nhiều so với tôi có thể.
Bây giờ hãy xem xét "hướng khác". Chúng ta có thể chia sẻ mã bằng cách tạo phiên bản không đồng bộ chỉ đơn giản là thực hiện phiên bản đồng bộ trên luồng công nhân không?
Đó là có thể và thực sự có thể là một ý tưởng tồi, vì những lý do sau đây.
Sẽ không hiệu quả nếu hoạt động đồng bộ là công việc IO có độ trễ cao. Điều này về cơ bản thuê một công nhân và làm cho công nhân đó ngủ cho đến khi một nhiệm vụ được thực hiện. Chủ đề là cực kỳ tốn kém . Theo mặc định, chúng tiêu thụ một triệu byte không gian địa chỉ, chúng mất thời gian, chúng chiếm tài nguyên hệ điều hành; bạn không muốn đốt cháy một chủ đề làm công việc vô ích.
Hoạt động đồng bộ có thể không được viết là luồng an toàn.
Đây là một kỹ thuật hợp lý hơn nếu công việc có độ trễ cao bị ràng buộc bởi bộ xử lý, nhưng nếu có thì có lẽ bạn không muốn đơn giản chuyển nó cho một luồng công nhân. Bạn có thể muốn sử dụng thư viện song song tác vụ để song song nó với càng nhiều CPU càng tốt, bạn có thể muốn logic hủy bỏ và bạn không thể đơn giản làm cho phiên bản đồng bộ làm tất cả điều đó, vì đó đã là phiên bản không đồng bộ .
Đọc thêm; một lần nữa, Stephen giải thích rất rõ ràng:
Tại sao không sử dụng Task.Run:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-USE.html
Nhiều kịch bản "làm và không" cho Nhiệm vụ.Run:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
Điều đó sau đó để lại cho chúng tôi những gì? Cả hai kỹ thuật để chia sẻ mã đều dẫn đến bế tắc hoặc thiếu hiệu quả. Kết luận mà chúng tôi đạt được là bạn phải đưa ra lựa chọn. Bạn có muốn một chương trình hiệu quả và chính xác và làm hài lòng người gọi hay bạn muốn lưu một vài tổ hợp phím được yêu cầu bằng cách sao chép một lượng nhỏ mã giữa các đường dẫn đồng bộ và không đồng bộ? Bạn không nhận được cả hai, tôi sợ.