2015-01-08 2 views
1

Я пытаюсь найти оптимальный способ выполнить следующие действия потокаОптимизация параллельного выбора, ведущий к Parallel excution

  1. Получить список идентификаторов X из потока
  2. Для каждого идентификатора X перейти к БД и получить список идентификаторов Y с использованием сложного запроса (должны быть подключены параллельно)
  3. Объединения списков и отчетливый его
  4. Для каждого идентификатора Y выполнить долго выполняющуюся задачу (должны быть подключены параллельно)

Весь поток в 2-4 должен выполняться в другом потоке от 1, так что как только действие 1 вызывает действие 2, оно продолжает делать другие вещи.

В настоящее время мой поток выглядит следующим образом (это на 2-4 и окликнул 1):

private void PerformActionAsync(List<long> XIds) 
{ 
    var affectedYIds = XIds.AsParallel().SelectMany(xId => 
    { 
     return GetAffectedYIdsLongRunning(xId); 

    }).Distinct(); 

    affectedYIds.ForAll(yId=> 
    { 
     ExcuteLongRunningAction(yId); 
    }); 
} 

Это не работает и как SelectMany и ForAll по-прежнему блокирует вызывающий поток, я могу заменить ForAll с новым созданием задачи, но все же SelectMany блокирует вызывающий поток. Как я могу выполнить SelectMany по-настоящему асинхронно?

В настоящее время мое лучшее решение заключается в том, чтобы обернуть реализацию всего метода Taks.Run, вопрос в том, есть ли лучший способ.

+0

вы искали '' Parallel.For' и Parallel.Foreach' также является 'Task.ContinueWith' ? –

+1

И Parallel.For, и Parallel.Foreach блокируют вызывающий поток, Task.ContinueWith не поддерживает объединение списков. Я обновил вопрос с более подробной информацией. – JoefGoldstein

+0

«Блокировка вызывающего потока» - это не то же самое, что «не выполняется параллельно». '.AsParallel()' является параллельным, но не асинхронным. Является ли 'GetAffectedYIdsLongRunning' самой асинхронной? Если нет, параллель - лучшее, что вы собираетесь получить - вы можете отложить получение результатов с помощью 'Task.Run()' и объявить 'PerformActionAsync', возвращая эту' Задачу'. –

ответ

1

Вы можете обернуть свой код в Task.Run и вернуть эту Задачу. Это заставит все работать в фоновом режиме.

Следующий тест Блок показывает использование:

[TestClass] 
public class PLinqTests 
{ 
    private static readonly Stopwatch Watch = Stopwatch.StartNew(); 

    [TestMethod] 
    public async Task TestPerformAsync() 
    { 
     await PerformActionAsync(Enumerable.Range(0, 10)); 
    } 

    private Task PerformActionAsync(IEnumerable<int> xIds) 
    { 
     return Task.Run(() => 
     { 
      var affectedYIds = xIds 
       .AsParallel() 
       .WithDegreeOfParallelism(5) 
       .SelectMany(this.GetAffectedYIdsLongRunning) 
       .Distinct(); 

      affectedYIds.ForAll(this.ExcuteLongRunningAction); 
     }); 
    } 

    private void ExcuteLongRunningAction(int yId) 
    { 
     Thread.Sleep(1000); 
     Console.WriteLine("Executed {0} at {1}.", yId, Watch.Elapsed.Seconds); 
    } 

    private IEnumerable<int> GetAffectedYIdsLongRunning(int xId) 
    { 
     Thread.Sleep(1000); 
     Console.WriteLine("Getting Affected for {0} at {1}.", xId, Watch.Elapsed.Seconds); 

     return Enumerable.Range(30, 10); 
    } 
} 

Выход:

Getting Affected for 0 at 1. 
Getting Affected for 1 at 1. 
Getting Affected for 2 at 1. 
Getting Affected for 4 at 2. 
Getting Affected for 3 at 2. 
Getting Affected for 5 at 2. 
Getting Affected for 6 at 2. 
Getting Affected for 7 at 3. 
Getting Affected for 8 at 3. 
Getting Affected for 9 at 3. 
Executed 32 at 3. 
Executed 31 at 3. 
Executed 30 at 4. 
Executed 34 at 4. 
Executed 33 at 4. 
Executed 37 at 4. 
Executed 36 at 4. 
Executed 35 at 5. 
Executed 39 at 5. 
Executed 38 at 5. 
Смежные вопросы