Задача. Продолжение не работает, как я ожидал - PullRequest
5 голосов
/ 09 августа 2011

Рассмотрим следующий код.Я начинаю с задачи, которая ничего не делает, а затем с помощью ContinueWith () запускаю 10 вызовов метода, увеличивающего счетчик.

Когда я запускаю эту программу, она печатает «0», указывая, что приращение() метод не был вызван вообще.Я ожидал, что он будет вызван 10 раз, поскольку именно столько раз я вызывал ContinueWith ().

Если я раскомментирую строку «Thread.Sleep (20)», то она печатает «10», как и ожидалось.

Это происходит в режиме выпуска или отладки.Моя система - это ядро ​​2 с четырьмя ядрами с гиперпоточностью (8 логических ядер) под управлением Windows 7 x64.

Я предполагаю, что у меня есть какое-то фундаментальное недоразумение о том, как работает Task.ContinueWith () ....

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main()
        {
            using (var task = Task.Factory.StartNew(()=>{}))
            {
                for (int i = 0; i < 10; ++i)
                {
                    task.ContinueWith(_=> increment());
                    // Thread.Sleep(20);  // Uncomment to print 10 instead of 0.
                }

                task.Wait();
            }

            // This prints 0 UNLESS you uncomment the sleep above.
            Console.WriteLine(counter); 
        }

        static void increment()
        {
            Interlocked.Increment(ref counter);
        }

        private static int counter;
    }
}

Кто-нибудь может пролить свет на то, что здесь происходит?

Ответы [ 3 ]

9 голосов
/ 09 августа 2011

Причина проста: вы ждете на задачу, которая уже выполнена. Что вам действительно нужно, так это дождаться десяти задач, которые вы создали в цикле:

var tasks = new List<Task>();
for (int i = 0; i < 10; ++i)
{
    tasks.Add(task.ContinueWith(_=> increment()));
}

Task.WaitAll(tasks.ToArray());
4 голосов
/ 09 августа 2011

Конечно.Вы распечатываете значение, когда задание initial завершено - вы не ожидаете продолжения.Другими словами, когда начальная задача завершается, происходит 11 событий:

  • 10 начинают выполняться 10 новых задач, каждая из которых увеличивает счетчик
  • Вы распечатываете счетчик

Вы можете эффективно объединить все это вместе, если хотите:

task = task.ContinueWith(_=> increment());

Затем, когда исходное задание завершится, сработает один инкрементатор.Когда , что заканчивается, срабатывает следующий инкрементатор.Когда это заканчивается [...].И когда последний инкрементатор закончится, ваш Wait вызов вернется.

3 голосов
/ 09 августа 2011

Если вы измените основной корпус следующим образом:

        using (var task = Task.Factory.StartNew(() => { }))
        {
            var t = task;
            for (int i = 0; i < 10; ++i)
            {
               t = t.ContinueWith(_ => increment());
                // Thread.Sleep(20);  // Uncomment to print 10 instead of 0.
            }

            t.Wait();
        }

Вы получите 10 без сна.Основное изменение - t = t.ContinueWith(...) и t необходимо, потому что вы не можете изменять переменную using().

...