【问题标题】:Is it possible to add to a list in c# asynchronously? [duplicate]是否可以异步添加到 c# 中的列表? [复制]
【发布时间】:2020-11-24 20:19:04
【问题描述】:

我正在 C# 中同步创建一个列表,但速度很慢。列表中大约有 50 个项目,添加每个项目最多可能需要 20 秒。添加项目的方法可以是异步的,所以我想知道是否可以使用 async/await 以某种方式添加所有项目。

这是一个将 2 个项目同步添加到列表的简单示例。我将如何异步执行?甚至可能吗?如果您尝试同时添加到列表中,您会遇到“冲突”吗?

using System.Threading;
using System.Collections.Generic;

class Program
{

    static void Main(string[] args)
    {
        List<int> lst = null;
        lst=GetList();
    }
    

    static List<int> GetList()
    {
        List<int> lst = new List<int>();

        //add to list
        lst.Add(Task1()); 
        lst.Add(Task2()); 

        //return list
        return lst;
    }

    static int Task1()
    {
        Thread.Sleep(10000);
        return 1; 
    }

    static int Task2()
    {
        Thread.Sleep(10000);
        return 2; 
    }

}

Compufreak 的答案正是我想要的。我有一个相关的问题,虽然我不确定这是否是我应该问的方式。我将 Compufreak 的代码稍微修改为一种对我来说更有意义的方式,我想知道代码的工作方式是否有任何实际差异。我从 Task1 和 Task2 中取出 async/await 并创建了一个 Task 并将 Task1 和 Task2 作为参数传入。

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;

class Program
{

    static async Task Main(string[] args)
    {
        List<int> lst = null;
        DateTime StartTime = DateTime.Now;
        lst = await GetList();
        DateTime EndTime = DateTime.Now;
        Console.WriteLine("ran tasks in " + (EndTime - StartTime).TotalSeconds + " seconds");
        Console.ReadLine();
    }


    static async Task<List<int>> GetList()
    {
        var tasks = new List<Task<int>>();

        //add task to list of tasks
        tasks.Add(new Task<int>(Task1));
        tasks.Add(new Task<int>(Task2));
        
        //start tasks
        foreach(Task<int> t in tasks)
        {
            t.Start();
        }

        //await results
        int[] results = await Task.WhenAll(tasks);

        //return list
        return results.ToList();
    }

    static int Task1()
    {
        Thread.Sleep(10000);
        return 1;
    }

    static int Task2()
    {
        Thread.Sleep(10000);
        return 2;
    }

}

【问题讨论】:

  • 您应该查看async/await。它是为这样的场景设计的(或其他 TPL 类型模式,如 Parallel.ForEach
  • @tnw 这似乎是一个不同的场景。在我的情况下,每个“添加”大约需要 10 秒来计算要添加的值。
  • @Andy 是的,我尝试使用 async/await 来实现,但我是新手,我什至不确定在这种情况下是否可行。
  • 看看下面的答案。这真是一个很好的例子。请记住,Async/Await 模式是一个非常深的兔子洞,但是,它在当今的编码标准中是无价的。最好早点学习。
  • 虽然链接的问题在我上次重新投票后发生了变化,但我仍然认为这不是 100% 的匹配。 now linked answer 是关于等待具有不同结果类型的任务。这是关于等待具有相等结果类型的任务。不同之处在于,在这种情况下,您可以直接使用Task.WhenAll 返回的数组,而不是像链接答案中那样单独处理结果。

标签: c# async-await


【解决方案1】:

不,您不能异步添加到列表中。

您需要在此处更改您的模式 - 如果您有许多长时间运行的任务并希望异步运行它们,您应该分别启动这些任务并随后收集/转换结果:

static async Task Main(string[] args)
{
    List<int> lst = null;
    DateTime StartTime = DateTime.Now;
    lst = await GetList();
    DateTime EndTime = DateTime.Now;
    Console.WriteLine("ran tasks in " + (EndTime - StartTime).TotalSeconds + " seconds");
 }


  static async Task<List<int>> GetList()
  {
      var tasks = new List<Task<int>>();
      tasks.Add(Task1());
      tasks.Add(Task2());
      int[] results = await Task.WhenAll(tasks);
      //return list
      return results.ToList();
  }

 static async Task<int> Task1()
 {
     await Task.Delay(10000);
     return 1;
 }

 static async Task<int> Task2()
 {
     await Task.Delay(10000);
     return 2;
 }

这将输出:

在 10,0727812 秒内运行任务

Here is a slightly modified fiddle to try it out online。我减少了延迟并在那里添加了一个额外的输出。

或者,您可以使用共享并发包在任务中添加结果(将结果添加到单独线程中的列表可能会导致奇怪的问题,例如空条目),但我认为在您的用例中没有充分的理由。

P.S.:您的函数的核心必须是异步的,否则您需要使用 Parallel.ForEach 之类的东西。如果可能,通常应该首选异步,因为它更有效地使用您的资源。

【讨论】:

  • 这看起来很有希望。让我试试那条路线。
  • 我尊重有人对此投了反对票 - 但下次请在 cmets 中留下解释,以便我将来改进。
  • 你的答案是 5 星!
【解决方案2】:

保持线程安全的关键是使用像 Progress 这样的 IProgress 实现并将该实例传递给您的任务。这样,所有线程都向管理器线程报告实际添加,但工作是在单独的线程上执行的。

开始任务之前

Progress prog = new Progress(p=>lst.Add(p));

任务功能

int Tast1(IProgess prog)
{
   int status = 0;
   Sleep(1000);
   prog.Report(1)
   status = 1;
   return status;
}

【讨论】:

  • 这比 async/await 容易吗?在我的情况下,异步/等待甚至可能吗?在开始尝试找出方法之前,我正在尝试确定最佳方法。
  • 如果每个线程只处理 1 个项目,那么您概述的内容就可以了。如果您希望多个线程在数据集上循环,那么 IProgress 将更新主线程。
猜你喜欢
  • 2019-01-30
  • 2018-01-19
  • 1970-01-01
  • 2013-09-22
  • 1970-01-01
  • 2022-06-10
  • 1970-01-01
  • 2019-09-13
  • 2020-08-22
相关资源
最近更新 更多