Отслеживание прогресса многошаговой задачи - PullRequest
4 голосов
/ 03 августа 2010

Я работаю на простом сервере, который предоставляет веб-сервисы клиентам. Некоторые из запросов могут занять много времени и логически разбиты на несколько этапов. Для таких запросов требуется сообщать о ходе выполнения. Кроме того, новый запрос может быть инициирован до завершения предыдущего, и требуется, чтобы оба выполнялись одновременно (за исключением некоторых системных ограничений).

Я думал о том, чтобы сервер возвратил TaskId своим клиентам, и чтобы клиенты отслеживали ход запросов с помощью TaskId. Я думаю, что это хороший подход, и я остаюсь с вопросом о том, как управляются задачи.

Никогда не используя TPL, я думал, что это будет хорошим способом решения этой проблемы. Действительно, это позволяет мне запускать несколько задач одновременно без необходимости вручную управлять потоками. Я даже относительно легко могу создавать многошаговые задачи, используя ContinueWith.

Хотя я не могу придумать хороший способ отслеживать ход выполнения задачи. Я понимаю, что когда мои запросы состоят из одного «шага», тогда этот шаг должен сообщать о своем состоянии совместно. Это то, что я предпочел бы избежать в этой точке. Однако, когда запрос состоит из нескольких этапов, я хотел бы знать, какой этап выполняется в настоящее время, и соответственно сообщить о ходе выполнения. Единственный способ, которым я мог придумать, является чрезвычайно утомительным:

Task<int> firstTask = new Task( () => { DoFirstStep(); return 3.14; } );
firstTask.
ContinueWith<int>( task => { UpdateProgress("50%"); return task.Result; } ).
ContinueWith<string>( task => { DoSecondStep(task.Result); return "blah"; }.
ContinueWith<string>( task => { UpdateProgress("100%"); return task.Result; } ).

И даже это не идеально, так как я хотел бы, чтобы Задача сохраняла свой собственный прогресс, вместо того, чтобы UpdateProgress обновлял какое-то известное местоположение. Кроме того, у него есть очевидный недостаток, заключающийся в необходимости менять множество мест при добавлении нового шага (поскольку теперь прогресс составляет 33%, 66%, 100% вместо 50%, 100%).

У кого-нибудь есть хорошее решение?

Спасибо!

Ответы [ 3 ]

5 голосов
/ 03 августа 2010

В действительности это не тот сценарий, когда библиотека параллельных задач полностью это поддерживает.

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

static void Main(string[] args)
{
    Example();
}

static BlockingCollection<Tuple<int, int, string>> _progressMessages = 
    new BlockingCollection<Tuple<int, int, string>>();

public static void Example()
{
    List<Task<int>> tasks = new List<Task<int>>();

    for (int i = 0; i < 10; i++)
        tasks.Add(Task.Factory.StartNew((object state) =>
            {
                int id = (int)state;
                DoFirstStep(id);
                _progressMessages.Add(new Tuple<int, int, string>(
                    id, 1, "10.0%"));
                DoSecondStep(id);
                _progressMessages.Add(new Tuple<int, int, string>(
                    id, 2, "50.0%"));

                // ...

                return 1;
            },
            (object)i
            ));

    Task logger = Task.Factory.StartNew(() =>
        {
            foreach (var m in _progressMessages.GetConsumingEnumerable())
                Console.WriteLine("Task {0}: Step {1}, progress {2}.",
                m.Item1, m.Item2, m.Item3);
        });


    List<Task> waitOn = new List<Task>(tasks.ToArray());
    waitOn.Add(logger);
    Task.WaitAll(waitOn.ToArray());
    Console.ReadLine();
}

private static void DoSecondStep(int id)
{
    Console.WriteLine("{0}: First step", id);
}

private static void DoFirstStep(int id)
{
    Console.WriteLine("{0}: Second step", id);
}

В этом образце не показаны отмены, обработка ошибок и учетная запись для вашего требования о том, что ваша задача может выполняться долго.Долгосрочные задачи предъявляют особые требования к планировщику.Более подробное обсуждение этого можно найти в http://parallelpatterns.codeplex.com/,, скачайте черновик книги и посмотрите Главу 3.

Это просто подход к использованию библиотеки параллельных задач в подобном сценарии.TPL вполне может не быть лучшим подходом здесь.

Если ваши веб-службы работают внутри ASP.NET (или аналогичного сервера веб-приложений), то вам также следует учитывать возможное влияниеиспользование потоков из пула потоков для выполнения задач, а не для обслуживания веб-запросов:

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

0 голосов
/ 25 октября 2013

Это может помочь: http://blog.stephencleary.com/2010/06/reporting-progress-from-tasks.html. В дополнение к отчету о прогрессе, это решение также позволяет обновлять элементы управления формой, не получая Межпотоковая операция не является допустимым исключением .

0 голосов
/ 03 августа 2010

Я не думаю, что решение, которое вы ищете, будет включать в себя Task API.Или, по крайней мере, не напрямую.Он не поддерживает понятие процента выполнения, и функции Task / ContinueWith должны участвовать в этой логике, потому что это данные, доступные только на этом уровне (только последний вызов ContinueWith находится в любой позиции, чтобы узнать процент завершения,и даже в этом случае алгоритмическое поведение будет в лучшем случае догадкой, поскольку оно точно не знает, займет ли одна задача намного больше времени, чем другая.выполнять реальную работу.

...