Cách tốt nhất để chuyển đổi phương thức không đồng bộ dựa trên cuộc gọi lại thành tác vụ có thể chờ đợi


79

Cách tốt nhất để chuyển đổi / bọc một phương thức không đồng bộ "cổ điển" sử dụng lệnh gọi lại một thứ gì đó trả về một Nhiệm vụ (có thể chờ đợi) là gì?

Ví dụ: đưa ra phương pháp sau:

public void GetStringFromUrl(string url, Action<string> onCompleted);

Cách duy nhất tôi biết để gói điều này thành một phương thức trả về một tác vụ là:

public Task<string> GetStringFromUrl(string url)
{
     var t = new TaskCompletionSource<string>();

     GetStringFromUrl(url, s => t.TrySetResult(s));

     return t.Task;
}

Đây có phải là cách duy nhất để đạt được điều này?

Và có cách nào để gói lời gọi đến GetStringFromUrl (url, gọi lại) trong chính tác vụ (tức là chính cuộc gọi sẽ chạy bên trong tác vụ thay vì đồng bộ)


2
BTW, đây không phải là phương thức không đồng bộ .net "cổ điển". Đó là BeginXxx()EndXxx()cặp. Ngoài ra, tại sao bạn đang tìm kiếm những cách khác để làm điều này? Bạn đang hy vọng đạt được gì?
svick

7
Tôi chỉ muốn đảm bảo rằng tôi không bỏ lỡ một số cách thay thế rõ ràng để làm điều tương tự.
Philippe Leybaert

có lý do gì để sử dụng TrySetResultthay vì SetResultở đây?
Ben Huber

Câu trả lời:


41

Mã của bạn ngắn, dễ đọc và hiệu quả, vì vậy tôi không hiểu tại sao bạn đang tìm kiếm các giải pháp thay thế, nhưng tôi không thể nghĩ ra bất cứ điều gì. Tôi nghĩ rằng cách tiếp cận của bạn là hợp lý.

Tôi cũng không rõ tại sao bạn nghĩ rằng phần đồng bộ là ổn trong phiên bản gốc, nhưng bạn muốn tránh nó trong phiên bản gốc Task. Nếu bạn cho rằng phần đồng bộ có thể mất quá nhiều thời gian, hãy sửa phần đó cho cả hai phiên bản của phương pháp.

Nhưng nếu bạn chỉ muốn chạy nó không đồng bộ (tức là trên ThreadPool) trong Taskphiên bản, bạn có thể sử dụng Task.Run():

public Task<string> GetStringFromUrl(string url)
{
    return Task.Run(() =>
    {
        var t = new TaskCompletionSource<string>();

        GetStringFromUrl(url, s => t.TrySetResult(s));

        return t.Task;
    });
}

1
Tôi không phải lúc nào cũng kiểm soát phương thức dựa trên lời gọi lại hiện có, vì vậy nếu phần đồng bộ của phương thức đang thực hiện điều gì đó mất quá nhiều thời gian, tôi cũng muốn có tùy chọn để di chuyển nó trong một tác vụ. Giải pháp của bạn chỉ giải quyết được điều đó.
Philippe Leybaert

1
Hmmm ... Tôi đã giả sử GetStringFromUrl ban đầu với lệnh gọi lại đã không đồng bộ (do đó lệnh gọi lại). Nếu vậy, gợi ý này sẽ đốt cháy các tài nguyên đang quay ra khỏi một tác vụ khác chỉ để thực hiện cuộc gọi.
Drew Marsh

@DrewMarsh Các phương thức thậm chí không đồng bộ thường có một số phần đồng bộ, mặc dù hầu hết thời gian nó không đáng kể. Nhưng câu hỏi đặc biệt yêu cầu cho điều đó, tôi sẽ không đề xuất bất cứ điều gì như vậy nếu không.
svick

1
@svick Chắc chắn, thường có một số chi phí khởi động, nhưng trừ khi ai đó tạo ra một bản triển khai thực sự kém chất lượng, tôi sẽ rất tiếc tiền mà bạn sẽ chi nhiều tiền hơn để ném nó sang một chuỗi khác với Task. luồng hiện tại. Tôi nghĩ rằng bạn phải tin tưởng vào API không đồng bộ mà bạn đang gọi về vấn đề đó cho đến khi bạn có thể chứng minh nó có tội và cá nhân tôi sẽ không bao giờ khuyên bạn làm điều đó theo mặc định. Chỉ là 2 ¢ của tôi.
Drew Marsh

Nếu phương thức này thực sự không đồng bộ thì việc bắt đầu một tác vụ mới sẽ làm mất tất cả các lợi thế của các công việc đang chạy không đồng bộ. Nếu muốn, bạn chỉ cần gọi Task.Yield () trước khi gọi phương thức để đăng nó vào bộ lập lịch tác vụ. Mã câu hỏi sẽ tốt hơn theo bất kỳ cách nào.
Alex Lyalka

5

Việc triển khai giả định của bạn là hoàn toàn tốt cho việc này giả sử lệnh gọi lại chỉ xử lý các tình huống thành công. Điều gì hiện đang xảy ra nếu một ngoại lệ xảy ra trong nền tảng không đồng bộ của việc triển khai GetStringFromUrl? Không có cách nào thực sự để họ tuyên truyền điều đó đến lệnh gọi lại Hành động ... họ chỉ nuốt nó và trả lại cho bạn giá trị không hay gì đó?

Điều duy nhất tôi muốn khuyên bạn là sử dụng theo quy ước mới về đặt tên như các phương thức async với hậu tố XXXAsync.

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.