Как выполнить несколько задач с помощью библиотеки параллельных задач и вернуться после первой, чтобы фактически вернуть данные - PullRequest
2 голосов
/ 18 марта 2012

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

Есть ли способ сделать что-то вроде «ContinueWhenAny», но только когда Task.Result удовлетворяет определенному условию, в противном случае дождитесь следующей задачи / и т. Д., Пока последняя задача не завершится?

Кроме того, мне нужно убедиться, что после того, как один результат верен, другие потоки отменяются. Эта часть уже работает нормально.

В настоящее время мой код выглядит следующим образом (без обработки исключений, только гайки и болты):

        ResultObject result = null;
        var tokenSource = new CancellationTokenSource();
        var tasks = listOfSources
                .Select(i => Task.Factory.StartNew(
                    () =>
                        {
                            i.CancellationToken = tokenSource.Token;
                            //Database Call
                            return i.getData(inputparameters);
                        }, tokenSource.Token));

        Task.Factory.ContinueWhenAny(
                tasks.ToArray(),
                firstCompleted =>
                    {
                        //This is the "result" I need to validate before setting and canceling the other threads
                        result = firstCompleted.Result;
                        tokenSource.Cancel();
                    }).Wait();
        return result;

Есть идеи? Я не хочу использовать ContinueWhenAll, поскольку, если первый вызов занимает 2 секунды, а второй - 10 секунд, я хочу вернуться к последовательному процессу через 2 секунды, если первый вызов возвращает действительные данные, в противном случае подождите 10 секунд, надеюсь, что Результат имеет действительные данные и возвращает недействительные данные только в том случае, если все задачи выполнены, и возвращает неверный результат.

--------- ОБНОВЛЕНИЕ ---- Спасибо zmbq за отличную идею. Обновленный (рабочий) код приведен ниже и отвечает всем моим требованиям. Однако, одно предостережение: разница между этим кодом и предыдущим кодом заключается в том, что этот код будет возвращать нулевой результат, если ни одна из задач не выдаст действительный результат, а не предыдущий код, который сам возвращает неверный результат. Нетрудно изменить эту версию, чтобы сделать это тоже, но я вполне доволен, возвращая в этом случае ноль для моих целей.

        ResultObject result = null;
        var tokenSource = new CancellationTokenSource();
        var tasks = listOfSources
                .Select(i => Task.Factory.StartNew(
                    () =>
                        {
                            i.CancellationToken = tokenSource.Token;
                            //Database Call
                            return i.getData(inputparameters);
                        }, tokenSource.Token)).ToArray();

        result = GetFirstValidResult(tokenSource,tasks);

        return result;


   private ResultObject GetFirstValidResult(CancellationTokenSource tokenSource, Task<ResultObject>[] tasks)
    {
        ResultObject result = null;
        Task.Factory.ContinueWhenAny(
            tasks,
            firstCompleted =>
                {
                    var testResult = firstCompleted.Result;
                    if(testResult != null && testResult.IsValid())
                    {
                        result = testResult;
                        tokenSource.Cancel();
                    }
                    else
                    {
                        var remainingTasks = tasks.Except(new[]{firstCompleted}).ToArray();
                        if(remainingTasks.Any())
                        {
                            result = GetFirstValidResult(tokenSource, remainingTasks);
                        } 
                    }
                }).Wait();
        return result;
    }

1 Ответ

4 голосов
/ 18 марта 2012

Хорошо, если ваш обратный вызов firstCompleted проверит результат и вызовет ContinueWhenAny для оставшихся задач в случае, если результат будет недопустимым, все будет сделано. ZeroMQ .Запустите задачи и попросите каждую задачу записать сообщение в очередь вывода, если ее результат допустим.Основной поток заблокируется в очереди и вернется, когда будет действительное сообщение или когда все задачи уже завершены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...