Чи рекомендується prevTask.Wait () використовувати з ContinueWith (з бібліотеки Завдань)?


88

Тож нещодавно мені сказали, що як я використовую свою програму .ContinueWith for Tasks, не є правильним способом їх використання. Я ще не мав знайти доказів цього в Інтернеті, тому я запитаю вас, хлопці, і подивлюсь, яка відповідь. Ось приклад того, як я використовую .ContinueWith:

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

Тепер я знаю, що це простий приклад, і він буде працювати дуже швидко, але просто припустимо, що кожне завдання виконує довшу операцію. Отже, те, що мені сказали, це те, що в .ContinueWith потрібно сказати prevTask.Wait (); інакше ви могли б виконати роботу до завершення попереднього завдання. Це взагалі можливо? Я припускав, що моє друге та третє завдання будуть виконуватися лише після завершення їх попереднього завдання.

Що мені сказали, як написати код:

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");
    });
}

Відповіді:


115

Е-е-е ... Я думаю, у деяких поточних відповідях чогось не вистачає: що відбувається за винятками?

Єдиною причиною, яку ви назвали б Waitу продовженні, було б спостерігати потенційний виняток із попереднього у самому продовженні. Те саме спостереження могло б статися, якщо ви отримали доступ Resultу випадку з a, Task<T>а також якщо ви здійснили доступ до Exceptionвласності вручну . Чесно кажучи, я б не дзвонив Waitі не отримував доступ, Resultтому що, якщо є виняток, ви заплатите ціну за його повторне підвищення, що зайві накладні витрати. Натомість ви можете просто перевірити IsFaultedвластивість за попередніми даними Task. Крім того, ви можете створювати розгалужені робочі процеси, прив’язуючи кілька продовжень братів і сестер, які запускаються лише на основі успіху або невдачі за допомогою TaskContinuationOptions.OnlyOnRanToCompletionта TaskContinuationOptions.OnlyOnFaulted.

Тепер необов’язково спостерігати за винятком попереднього в продовженні, але ви можете не хотіти, щоб ваш робочий процес рухався вперед, якщо, скажімо, «Крок 1» не вдався. У такому випадку: вказівка TaskContinuationOptions.NotOnFaultedна ваші ContinueWithдзвінки не дозволить логіці продовження коли-небудь навіть спрацювати.

Майте на увазі, що, якщо у ваших власних продовженнях не спостерігається виняток, особа, яка чекає на завершення цього загального робочого процесу, буде його спостерігати. Або вони перебувають Waitу Taskверхній течії, або взяли участь у власному продовженні, щоб знати, коли воно завершиться. Якщо це останнє, для їх продовження потрібно буде використовувати вищезазначену логіку спостереження.


2
Нарешті хтось дасть правильну відповідь. @ Travyguy9 Будь ласка, прочитайте цю відповідь @DrewMarsh і прочитайте більше проTaskContinuationOptions
Джаспер

2
Чудова відповідь, я шукав "Майте на увазі, що якщо ваші власні продовження не спостерігають винятку, особа, яка чекає на завершення цього загального робочого процесу, буде тим, хто його спостерігатиме". Хоча одне питання, коли вашого завдання не чекають, хто є офіціантом за замовчуванням? (Не вдалося знайти відповіді на це)
Тібо Д.,

20

Ви використовуєте його правильно.

Створює продовження, яке виконується асинхронно, коли цільове Завдання виконується .

Джерело: Task.ContinueWith Method (Дія як MSDN)

Виклик prevTask.Wait()у кожному Task.ContinueWithвиклику здається дивним способом повторення непотрібної логіки - тобто робити щось, щоб бути «надмірно впевненим», оскільки ви насправді не розумієте, що робить певний біт коду. Як перевірка нуля просто для того, щоб кинути ArgumentNullExceptionтуди, куди б його все одно кинули.

Отже, ні, той, хто сказав вам, що це неправильно, і, мабуть, не розуміє, чому Task.ContinueWithіснує.


16

Хто тобі це сказав?

Цитування MSDN :

Створює продовження, яке виконується асинхронно, коли цільове Завдання виконується.

Крім того, якою буде мета Продовжити, якби вона не чекала завершення попереднього завдання?

Ви навіть можете протестувати це самостійно:

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

З MSDN даліTask.Continuewith

Повернуте Завдання не планується виконувати, поки поточне завдання не буде завершено. Якщо критерії, зазначені через параметр continueOptions, не виконуються, завдання продовження буде скасовано замість запланованого.

Я думаю, що те, як ви очікуєте, що це спрацює в першому прикладі, є правильним.


2

Ви також можете розглянути можливість використання Task.Run замість Task.Factory.StartNew.

Повідомлення в блозі Стівена Клірі та повідомлення Стівена Туба, на яке він посилається, пояснюють відмінності. У цій відповіді також є дискусія .


4
Проголосовано проти, оскільки воно не стосується фактичного питання. Це додає певної цінності, але має бути коментарем.
Sinaesthetic

0

Отримуючи доступ, Task.Resultви фактично робите подібну логікуtask.wait


Так. Ми можемо уникнути методу Wait (). Але це працює лише з результатними завданнями, наприклад, Завдання <bool>
Олександр Улмаскулов

Проголосовано проти, оскільки воно не стосується фактичного питання. Це додає певної цінності, але має бути коментарем.
Sinaesthetic

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.