运行Async Foreach循环C#异步等待

时间:2017-01-10 04:54:14

标签: c# asynchronous async-await

我正在努力掌握c#async等待的基本概念。

基本上我所拥有的是一个我需要处理的对象列表,处理涉及迭代其属性并连接字符串,然后创建一个新对象(在本例中称为trellocard)并最终添加一个trellocards列表

迭代需要很长时间,所以我想做的是异步处理多个对象。

我尝试了多种方法,但基本上我想做这样的事情。 (在下面的例子中我已经删除了处理,只是把system.threading.thread.sleep(200)。我等待这不是一个异步方法,我可以使用tasks.delay,但重点是我的处理没有有任何异步方法,我想用多个实例运行整个方法。

    private async Task<List<TrelloCard>> ProcessJobs(IQueryable<IGrouping<CardGrouping, Job>> jobs)
    {
        List<TrelloCard> cards = new List<TrelloCard>();

        foreach (var job in jobs.ToList())
        {
           card = await ProcessCards(job, cards);  // I would like to run multiple instances of the processing
           cards.add(card); //Once each instance is finshed it adds it to the list
        }


  private async Task<TrelloCard> ProcessCards(Job job)
    {
        System.Threading.Thread.Sleep(2000);  //Just for examples sake

        return new TrelloCard();
    }

3 个答案:

答案 0 :(得分:3)

  

我正在努力掌握c#async等待的基本概念。

简单的定义是,Async-Await是一部分.Net并发,可用于进行多个IO调用,并且在进程中不会浪费线程,这是用于计算操作的。它就像调用数据库,Web服务,网络调用,文件IO,所有这些都不需要当前的进程线程

在您目前的情况下,用例是:

  1. 遍历其属性并连接字符串,然后创建新对象
  2. 最终添加了一个trellocards列表
  3. 这似乎是一个计算绑定操作,除非你正在做一个IO,否则看起来你正在遍历一个内存对象,对于这种情况,更好的选择是:

    1. Parallel.ForEach,为了并行化内存处理,虽然你需要注意Race条件,因为给定的内存可以由多个线程访问,因此在写入操作期间特别破坏它,所以至少在当前代码使用来自ConcurrentBag命名空间的System.Collections.Concurrent的线程安全集合,或者适合用例而不是List<TrelloCard>的线程安全集合,或者您可以考虑关注Thread safe list
    2. 另请注意,如果您的方法不是默认情况Async,那么您可以计划将它们包装在Task.Run中,await,尽管这需要一个线程池线程,但可以使用Async-Await

      进行调用

      Parallel.Foreach代码用于您的用例(我正在进行直接替换,您的代码中似乎存在问题,因为ProcessCards函数,只需要获取Job对象,但您也传递了集合{ {1}},这是编译错误):

      Cards

答案 1 :(得分:3)

如果您希望它们并行运行,您可以为每个操作生成一个新任务,然后使用Task.WhenAll等待所有操作完成。

private async Task<List<TrelloCard>> ProcessJobs(IQueryable<IGrouping<CardGrouping, Job>> jobs)
{
    List<Task<TrelloCard>> tasks = new List<Task<TrelloCard>>();

    foreach (var job in jobs)
    {
        tasks.Add(ProcessCards(job));
    }

    var results = await Task.WhenAll(tasks);

    return results.ToList();
}


private Task<TrelloCard> ProcessCards(Job job)
{
    return Task.Run(() =>
    {
        System.Threading.Thread.Sleep(2000);  //Just for examples sake

        return new TrelloCard();
    });
}

答案 2 :(得分:1)

jobs.ToList()只是在浪费记忆力。它已经IEnumerable,因此可以在foreach中使用。

ProcessCards无法编译。你需要这样的东西

    private Task<TrelloCard> ProcessCards(Job job)
    {
        return Task.Run(() =>
        {
            System.Threading.Thread.Sleep(2000);  //Just for examples sake

            return new TrelloCard();
        });
    }

现在您需要ProcessJobs

  • 为每个作业创建ProcessCards任务
  • 等待所有任务完成
  • 返回一系列TrelloCard

    private async Task<List<TrelloCard>> ProcessJobs(IQueryable<IGrouping<CardGrouping, Job>> jobs)
    {
        return await Task.WhenAll(jobs.Select(ProcessCards));
    }