2015-11-24 4 views
1

У меня есть три класса:TPL проблема управления потоком

BargeGrouper класс, который реализует IBargeGrouper и имеет Group(IEnumerable<Barge> ungrouped) метод.

BargeGroupMap класс, который реализует IBargeGroupShow и имеет Show(IEnumerable<BargeGroup> метод.

И класс, в котором я называю оба, если они: GroupingModule и это Run().

Проблема заключается в том, что, когда я называю Group(ungrouped); и затем хочу Show(bargeGroups); я получил IndexOutOfRange исключения в Show(bargeGroups); потому что IEnumerable я перехожу к нему в качестве параметра имеет свойство Count гораздо больше, чем фактические элементы в нем. Я узнал, что сгруппированная коллекция возвращается как пусто от Group большую часть времени, несмотря на то, что я использую ContinueWith, чтобы заполнить эту коллекцию с помощью элементов BargeGroup.

Group метод BargeGrouper

public IEnumerable<BargeGroup> Group(IEnumerable<Barge> ungroupedBarges) 
{ 
    List<BargeGroup> bargeGroups = new List<BargeGroup>(); 
    Int32 groupNumber = 0; 

    var riverBarges = from barge in ungroupedBarges 
         where barge != null && !String.IsNullOrEmpty(String.Intern(barge.River)) 
         let river = String.Intern(barge.River) 
         orderby river, barge.MileMarker ascending 
         group barge by river into barges 
         select barges.AsEnumerable(); 

    foreach (IEnumerable<Barge> barges in riverBarges) 
    { 
     Task.Run(() => ResolveRiver(barges, ref groupNumber)).ContinueWith(t=> 
     { 
      IEnumerable<BargeGroup> riverGroups = t.Result; 
      bargeGroups.AddRange(riverGroups); 
     }); 
    } 

    return bargeGroups; 
} 

ShowBargeGroups:

public void ShowBargeGroups(IEnumerable<BargeGroup> bargeGroups) 
{ 
    Console.WriteLine("Barge groups :"); 
    if (bargeGroups != null) 
    { 
     foreach (var group in bargeGroups.Where(b => b != null)) 
     { 
      var title = String.Format("{0}\n\t | {1} \t\t | {2} \t | {3} \t|", group.Id, "Id", "River", "MileMarker"); 
      Console.WriteLine(title); 
      foreach (var barge in group.Barges) 
      { 
       var caption = String.Format("\t | {0}\t | {1} \t | {2} \t|", barge.Id, barge.River, barge.MileMarker); 
       Console.WriteLine(caption); 
      } 
     } 
    } 
} 

А использование в GroupingModule:.

var groupBarges = bargeGrouper.Group(barges);

bargeGroupShow.ShowBargeGroups(groupBarges);

Что я могу сделать, чтобы исправить это?

+0

Покажите нам 'ShowBargeGroups'. Какую версию .NET вы используете? –

+0

@YuvalItzchakov, пожалуйста, см. Обновление. Я использую .net 4.5. Спасибо. – Sega

ответ

0

Нет гарантии, что к моменту возврата из метода GroupTask будет обработан список bargeGroups. Я подозреваю, что ваш метод ResolveRiver может занять некоторое время.

Вам нужно подождать, пока задача завершится, прежде чем возвращать перечислимое значение от Group. Я предлагаю сделать Groupасинхронной и ожидают для Task:

public async Task<IEnumerable<BargeGroup>> Group(IEnumerable<Barge> ungroupedBarges) 
{ 
    List<BargeGroup> bargeGroups = new List<BargeGroup>(); 
    Int32 groupNumber = 0; 

    var riverBarges = from barge in ungroupedBarges 
         where barge != null && !String.IsNullOrEmpty(String.Intern(barge.River)) 
         let river = String.Intern(barge.River) 
         orderby river, barge.MileMarker ascending 
         group barge by river into barges 
         select barges.AsEnumerable(); 

    foreach (IEnumerable<Barge> barges in riverBarges) 
    { 
     await Task.Run(() => ResolveRiver(barges, ref groupNumber)).ContinueWith(t=> 
     { 
      IEnumerable<BargeGroup> riverGroups = t.Result; 
      bargeGroups.AddRange(riverGroups); 
     }); 
    } 

    return bargeGroups; 
} 

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

Если вы не хотите идти асинхр/ждут, как вы можете просто .Wait()Task.

1

Task.Run не выполняет свою работу немедленно. Итак, по крайней мере, у вас в вашем коде есть состояние гонки. Попробуйте так:

public async Task<IEnumerable<BargeGroup>> Group(IEnumerable<Barge> ungroupedBarges) 
{ 
    List<BargeGroup> bargeGroups = new List<BargeGroup>(); 
    Int32 groupNumber = 0; 

    var riverBarges = from barge in ungroupedBarges 
         where barge != null && !String.IsNullOrEmpty(String.Intern(barge.River)) 
         let river = String.Intern(barge.River) 
         orderby river, barge.MileMarker ascending 
         group barge by river into barges 
         select barges.AsEnumerable(); 

    foreach (IEnumerable<Barge> barges in riverBarges) 
    { 
     var riverGroups = await Task.Run(() => ResolveRiver(barges, ref groupNumber)); 
     bargeGroups.AddRange(riverGroups); 
    } 

    return bargeGroups; 
} 
2

При использовании Task.Run, который возвращает Task без асинхронно ожидания на него, выполнение следующей строки будет происходить сразу, он не будет ждать, пока делегат передается для завершения.

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

public Task<IEnumerable<BargeGroup>> GroupAsync(IEnumerable<Barge> ungroupedBarges) 
{ 
    // Do the grouping 

    var riverTasks = riverBarges.Select(barges => 
             Task.Run(ResolveRiver(barges, ref groupNumber))); 

    var result = await Task.WhenAll(riverTasks); 
    bargeGroups.AddRange(result.Result.SelectMany(x => x)); 
    return bargeGroups; 
} 

И потреблять его так:

public async Task FooAsync() 
{ 
    var barges = await GroupAsync(ungroupedBarges); 
    ShowBargeGroups(barges); 
} 

Примечание I 'Будьте осторожны с параметром ref, который передается и вызывается параллельно, что не будет безопасным. Если вы можете удалить groupNumber из уравнения, сделайте это.

+0

Имеет ли свойство riverTasks свойство Result? – Sega

+0

@Sega Нет, это не так. Я изменил код, спасибо за этот комментарий. –

0

Task.Run в вашем коде создает еще один ожидаемый, который, скорее всего, будет выполнен после возвращения вашей функции. Самое простое решение - подождать его с помощью функции Wait.

Task.Run(() => ResolveRiver(barges, ref groupNumber)).ContinueWith(t=> 
    { 
     IEnumerable<BargeGroup> riverGroups = t.Result; 
     bargeGroups.AddRange(riverGroups); 
    }).Wait() 

или событие лучше, вам не нужно Task.Run, ожидая продолжения функции будет достаточно:

ResolveRiver(barges, ref groupNumber)).ContinueWith(t=> 
    { 
     IEnumerable<BargeGroup> riverGroups = t.Result; 
     bargeGroups.AddRange(riverGroups); 
    }).Wait(); 
Смежные вопросы