Có khuyến nghị dùng presetask.Wait () với ContinueWith (từ thư viện Nhiệm vụ) không?


88

Vì vậy, gần đây tôi đã được cho biết rằng cách tôi sử dụng .ContinueWith for Tasks không phải là cách thích hợp để sử dụng chúng. Tôi vẫn chưa tìm thấy bằng chứng về điều này trên internet nên tôi sẽ hỏi các bạn và xem câu trả lời là gì. Đây là một ví dụ về cách tôi sử dụng .ContinueWith:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
}

Bây giờ tôi biết đây là một ví dụ đơn giản và nó sẽ chạy rất nhanh, nhưng chỉ cần giả sử mỗi tác vụ thực hiện một số hoạt động lâu hơn. Vì vậy, những gì tôi đã được nói là trong .ContinueWith, bạn cần phải nói presTask.Wait (); nếu không, bạn có thể làm việc trước khi tác vụ trước đó kết thúc. Điều đó thậm chí có thể? Tôi đã giả định rằng nhiệm vụ thứ hai và thứ ba của mình sẽ chỉ chạy khi nhiệm vụ trước đó của chúng kết thúc.

Những gì tôi đã được cho biết cách viết mã:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 3");
    });
}

Câu trả lời:


115

Ehhh .... Tôi nghĩ rằng một số câu trả lời hiện tại đang thiếu một số câu hỏi: điều gì xảy ra với các trường hợp ngoại lệ?

Lý do duy nhất mà bạn gọi Waittrong phần tiếp diễn là để quan sát một ngoại lệ tiềm năng từ phần trước trong chính phần tiếp theo. Quan sát tương tự sẽ xảy ra nếu bạn truy cập Resulttrong trường hợp a Task<T>và cả nếu bạn truy cập thuộc Exceptiontính theo cách thủ công . Thành thật mà nói, tôi sẽ không gọi Waithoặc truy cập Resultbởi vì nếu có ngoại lệ, bạn sẽ phải trả giá bằng việc nâng cấp lại nó là chi phí không cần thiết. Thay vào đó, bạn có thể chỉ cần kiểm tra thuộc IsFaultedtính khỏi tiền trước Task. Ngoài ra, bạn có thể tạo quy trình công việc được phân nhánh bằng cách xâu chuỗi trên nhiều liên tục anh em ruột chỉ kích hoạt dựa trên thành công hoặc thất bại với TaskContinuationOptions.OnlyOnRanToCompletionTaskContinuationOptions.OnlyOnFaulted.

Bây giờ, không cần thiết phải quan sát ngoại lệ của tiền đề trong phần tiếp theo, nhưng bạn có thể không muốn quy trình làm việc của mình tiếp tục nếu, chẳng hạn "Bước 1" không thành công. Trong trường hợp đó: việc chỉ định TaskContinuationOptions.NotOnFaultedcho các ContinueWithcuộc gọi của bạn sẽ ngăn không cho logic tiếp tục kích hoạt.

Hãy nhớ rằng, nếu quy trình liên tục của riêng bạn không quan sát được ngoại lệ, người đang đợi quy trình công việc tổng thể này hoàn thành sẽ là người quan sát nó. Hoặc là họ đang Waiting trên Taskthượng nguồn hoặc đã tacked trên tiếp tục riêng của họ để biết khi nào nó hoàn tất. Nếu là cái sau, việc tiếp tục của họ sẽ cần sử dụng logic quan sát đã nói ở trên.


2
Cuối cùng ai đó đưa ra một câu trả lời chính xác. @ Travyguy9 Vui lòng đọc câu trả lời @DrewMarsh này và đọc thêm vềTaskContinuationOptions
Jasper

2
Câu trả lời tuyệt vời, tôi đang tìm kiếm "Hãy nhớ rằng, nếu sự liên tục của riêng bạn không quan sát thấy ngoại lệ, người đang đợi quy trình công việc tổng thể này hoàn thành sẽ là người quan sát nó." Tuy nhiên, một câu hỏi đặt ra, khi nhiệm vụ của bạn không được chờ đợi, ai là người phục vụ mặc định? (Không thể tìm thấy câu trả lời cho điều này)
Thibault D.

20

Bạn đang sử dụng nó một cách chính xác.

Tạo phần tiếp theo thực thi không đồng bộ khi Nhiệm vụ mục tiêu hoàn thành.

Nguồn: Task.ContinueWith Method (Hành động như MSDN)

Việc phải gọi prevTask.Wait()trong mọi lệnh Task.ContinueWithgọi dường như là một cách kỳ lạ để lặp lại logic không cần thiết - tức là làm điều gì đó để trở nên "siêu chắc chắn" bởi vì bạn thực sự không hiểu một đoạn mã nào đó làm gì. Giống như kiểm tra null chỉ để ném một ArgumentNullExceptionnơi mà nó sẽ được ném đi.

Vì vậy, không, bất cứ ai nói với bạn điều đó là sai và có thể không hiểu tại sao Task.ContinueWithtồn tại.


16

Ai nói với bạn rằng?

Trích dẫn MSDN :

Tạo phần tiếp theo thực thi không đồng bộ khi Nhiệm vụ mục tiêu hoàn thành.

Ngoài ra, mục đích của Tiếp tục với sẽ là gì nếu nó không đợi nhiệm vụ trước đó được hoàn thành?

Bạn thậm chí có thể tự mình kiểm tra nó:

Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
        Thread.Sleep(2000);
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("I waited step 1 to be completed!");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });

5

Từ MSDN trênTask.Continuewith

Tác vụ đã trả lại sẽ không được lên lịch thực hiện cho đến khi tác vụ hiện tại đã hoàn thành. Nếu không đáp ứng các tiêu chí được chỉ định thông qua tham số ContinuationOptions, tác vụ tiếp tục sẽ bị hủy bỏ thay vì được lên lịch.

Tôi nghĩ rằng cách bạn mong đợi nó hoạt động trong ví dụ đầu tiên là cách chính xác.



0

Bằng cách Truy cập, Task.Resultbạn thực sự đang thực hiện logic tương tự vớitask.wait


Đúng. Chúng ta có thể tránh phương thức Wait (). Nhưng nó hoạt động với nhiệm vụ kết quả duy nhất, ví dụ như công tác <bool>
Alexander Ulmaskulov

Bỏ phiếu vì nó không giải quyết được câu hỏi thực tế. Nó thêm một số giá trị, nhưng phải là một bình luận.
Sinaesthetic

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.