2015-04-21 3 views
12

У меня есть следующий код: синхронногоПреобразование цикла к задачам

foreach (var step in result) { 
    step.Run(); 
} 

Я попытался преобразовать его задачи, но я не в состоянии сделать это. Я попытался преобразовать его с помощью Task.WhenAll, как это (и я добавить асинхр к подписи метода):

var tasks = new List<Task>(); 
foreach (var step in result) { 
    tasks.Add(new Task(() => step.Run())); 
} 
await Task.WhenAll(tasks); 

Это возвращает немедленно и не выполняет метод Run(). Затем я попытался преобразовать его в следующий код:

var tasks = new List<Task>(); 
foreach (var step in result) { 
    tasks.Add(new Task(() => step.Run())); 
} 
var task = Task.WhenAll(tasks); 
task.Wait(); 

Это блокировка навсегда. Тем не менее, когда я создаю внутри цикла он работает:

foreach (var step in result) { 
    var t = Task.Run(() => step.Run()); 
    t.Wait(); 
} 

Если я использую вместо await Task.Run(() => step.Run()); он ждет только первый и возобновляет основной поток.

метод

Прогон выглядит следующим образом:

public async void Run() { 
    var result = Work(); 
    if (null != result && result.Count > 0) { 
     var tasks = new List<Task>(); 
     foreach (var step in result) { 
      await Task.Run(() => step.Run()); 
     } 
    } 
} 

Все этапы реализации способа работы() (который является абстрактным в базовом классе). Мой первый шаг выглядит следующим образом:

class NoWorkStep : WorkerStep { 
    protected override IList<WorkerStep> Work() { 
     Console.WriteLine("HERE"); 
     List<WorkerStep> newList = new List<WorkerStep>(); 
     for (int i = 0; i < 10; i++) { 
      newList.Add(new NoWorkStep2()); 
     } 
     return newList; 
    } 
} 

И мой второй шаг выглядит следующим образом:

class NoWorkStep2 : WorkerStep { 
    protected override IList<WorkerStep> Work() { 
     Console.WriteLine("HERE-2"); 
     return new List<WorkerStep>(); 
    } 
} 

я просто создать экземпляр NoWorkStep и вызвать instance.Run().

Где у меня проблема с выполнением шагов с помощью Task.WhenAll?

Edit: Вызов кода после того, как я изменил метод Run для async Task RunAsync:

private static async void doIt() { 
    var step = new NoWorkStep(); 
    await step.RunAsync(); 
} 
+1

async _void_ Run()? – Ewan

+0

Не используйте 'new Task'. Причина, по которой ваш четвертый пример работает, заключается не в том, что 'Wait' находится внутри цикла - он работает, потому что вы используете' Task.Run' вместо 'new Task'. – Luaan

ответ

20

Позволяет наметить проблемы с кодом:

new Task(() => step.Run()) 

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

new Task(() => step.Run()).Start) 

Но, вы не должны использовать new Task в любом случае, вы должны использовать Task.Run.

Если я использую вместо этого ждут Task.Run (() => step.Run()); он ждет только первый и возобновляет основной поток.

Это потому, что Runasync void, что не может быть ожидаемо. async void предназначен для использования только в обработчиках событий верхнего уровня, где это явно не так.

Если вы хотите, чтобы ждать, пока все задачи не будут выполнены, вы можете сделать следующее:

public async Task RunAsync() 
{ 
    var result = Work(); 
    var stepTasks = result.Select(step => Task.Run(() => step.Run())); 
    await Task.WhenAll(steps); 
} 

Это будет гарантировать все задачи завершили выполнение раз RunAsync отделки.

+0

Я изменил 'async void Run' на' async Task RunAsync() 'и использовал ваш фрагмент кода. Я называю RunAsync, используя 'await step.RunAsync();' и, к сожалению, иногда он печатает десять раз ЗДЕСЬ-2, иногда два раза иногда нет ЗДЕСЬ-2. – Sascha

+0

@Sascha Покажите мне, как вы вызываете этот код. –

+0

@Sascha Кто называет 'doIt'? и как? –

6

Вы, кажется, не начинаете задачи.

Try:

var tasks = new List<Task>(); 

foreach (var step in result) 
{ 
    var t = new Task(() => step.Run()); 
    t.Start(); 
    tasks.Add(t); 
} 

Task.WhenAll(tasks); 
+2

Просто вызовите Task.Run, нет необходимости создавать холодную задачу, чтобы запустить ее в следующей строке. –

+0

Я просто пытался проиллюстрировать, почему код в вопросе не работал. – RagtimeWilly

+0

Холодные задачи были реальной проблемой с исходным кодом. Легко забыть вызов Start, и они ничего не предлагают над «Task.Run» или «Task.Factory.StartNew». –

6

Вы можете использовать Parallel.ForEach.

Parallel.ForEach(result, step => step.Run()); 

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

+2

Это сработает, и, возможно, я закончу это, но шаблон aync-await тоже должен работать, и мне нравится понимать, где у меня ошибка. – Sascha

+0

Или действительно 'result.AsParallel(). ForAll (step => step.Run());', но вы бы не использовали PLINQ, если у вас не было реального запроса. –

+0

@NathanCooper Вы правы, это зависит от типа «результата», если это имеет смысл или нет. Оба работают, но мне нравится 'ForEach()' здесь, потому что он сохраняет намерение исходного фрагмента кода. – jdphenix

Смежные вопросы