TPL控制流程问题

时间:2015-11-24 09:45:27

标签: c# async-await task-parallel-library task

我有三个班级:

BargeGrouper类,它实现IBargeGrouper并具有Group(IEnumerable<Barge> ungrouped)方法。

BargeGroupMap类,它实现IBargeGroupShow并具有Show(IEnumerable<BargeGroup>方法。

我打电话给他们的班级,如果这些是:GroupingModule,那就是Run()

问题在于,当我致电Group(ungrouped);然后想要Show(bargeGroups);时,IndexOutOfRange中出现Show(bargeGroups);例外情况,因为IEnumerable我将其作为传递给Count参数的Group属性比其中的实际元素多得多。我发现尽管我使用ContinueWithBargeGroup元素填充该集合,但大多数情况下,分组集合从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);

def team_member_params params.require(:team_member).permit(:name, :id_steam, :color, :avatar, :avatar_color, :description, :rank_cs, :rank_lol, :role_cs, :role_lol, team_members_games: [ :id, :name_game]) end

我该怎么做才能解决这个问题?

4 个答案:

答案 0 :(得分: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);
}

注意我要小心传递和并行调用的ref参数,这样做不会安全。如果您可以从等式中删除groupNumber,请执行此操作。

答案 1 :(得分: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 :(得分:0)

无法保证当您从Group方法返回时,Task已完成处理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异步并等待它。然后,您可以摆脱用于与bargeGroups通信的List Task

如果您不想采用 async / await 方式,可能只需.Wait() Task

答案 3 :(得分: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();