代码之家  ›  专栏  ›  技术社区  ›  Junior

我是否可以在后台运行多个慢速进程,以便多个任务可以并行运行?

  •  4
  • Junior  · 技术社区  · 5 年前

    我有一个使用 C# 在核心.NET2.2框架的顶部。

    我的应用程序允许我使用Windows任务调度器触发长时间运行的管理作业。

    其中一个管理作业进行web API调用,在将大量文件上载到Azure Blob存储之前下载这些文件。以下是我的代码完成任务所需执行的逻辑步骤

    1. 调用远程API,该API使用Mime消息进行响应,其中每条消息表示一个文件。
    2. 解析Mime消息并将每条消息转换为 MemoryStream

    有一次我收集了1000个+ 记忆流 Stream

    我的理解是,调用异步写入/上传流的代码正好可以做到这一点。换句话说,我会说“有一个 执行它并运行它所需要的时间。只要任务完成,我就不在乎结果。”

    在测试过程中,我发现我对通话的理解 async 这有点无效。我的印象是,当调用一个用 异步的 将在后台线程/工作线程中执行,直到该进程完成。但是,当我测试代码时,我的理解失败了。我的代码显示,没有添加关键字 await 异步的 代码从未真正执行过。同时,当关键字 等候 等候

    这里是我的代码的精简版本,以解释我试图实现的目标

    public async Task Run()
    {
        // This gets populated after calling the web-API and parsing out the result
        List<Stream> files = new List<MemoryStream>{.....};
    
        foreach (Stream file in files)
        {
            // This code should get executed in the background without having to await the result
            await Upload(file);
        }
    }
    
    // This method is responsible of upload a stream to a storage and log error if any
    private async Task Upload(Stream stream)
    {
        try
        {
            await Storage.Create(file, GetUniqueName());
        } 
        catch(Exception e)
        {
            // Log any errors
        }
    }
    

    从上面的代码中,调用 await Upload(file); 工作正常,将按预期上传文件。但是,由于我使用 等候 打电话给 Upload() 方法,在上载代码完成之前,我的循环不会跳转到下一个迭代。同时,卸下 等候 关键字,循环不会等待上载过程,但流从未实际写入存储,就好像我从未调用代码一样。

    如何执行多个 Upload

    5 回复  |  直到 5 年前
        1
  •  7
  •   Blue    5 年前

    将列表转换为“上传”任务列表,并等待所有任务完成 Task.WhenAll() :

    public async Task Run()
    {
        // This gets populated after calling the web-API and parsing out the result
        List<Stream> files = new List<MemoryStream>{.....};
        var tasks = files.Select(Upload);
    
        await Task.WhenAll(tasks);
    }
    

    this post 有关任务/等待的更多信息。

        2
  •  4
  •   Erik Philips    5 年前

    我希望我可以使用自己的进程或线程来执行每个写迭代。

    这并不是最好的方法。进程和线程是有限的资源。你的 限制因素

    您要做的是:

    var tasks = new List<Task>(queue.Count);
    
    while (queue.Count > 0)
    {
      var myobject = Queue.Dequeue();
      var task = blockBlob.UploadFromByteArrayAsync(myobject.content, 0, myobject.content.Length);
      tasks.Add(task);
    }
    await Task.WhenAll(tasks);
    

    在这里,我们只是尽可能快地创建任务,然后等待它们全部完成。我们将让.Net框架来处理其余的部分。

        3
  •  3
  •   abatishchev Marc Gravell    5 年前

    您可能需要:

    var tasks = files.Select(Upload);
    await Task.WhenAll(tasks);
    

    请注意,它将产生与文件数量一样多的任务,如果任务太多,可能会导致进程/机器停机。看见 Have a set of Tasks with only X running at a time

        4
  •  3
  •   TheGeneral    5 年前

    其他答案很好,但另一种方法是解决您的问题 可从以下网址获得Nuget https://www.nuget.org/packages/System.Threading.Tasks.Dataflow/

    public static async Task DoWorkLoads(List<Something> results)
    {
       var options = new ExecutionDataflowBlockOptions
                         {
                            MaxDegreeOfParallelism = 50
                         };
    
       var block = new ActionBlock<Something>(MyMethodAsync, options);
    
       foreach (var result in results)
          block.Post(result );
    
       block.Complete();
       await block.Completion;
    
    }
    
    ...
    
    public async Task MyMethodAsync(Something result)
    {       
       //  Do async work here
    }
    

    1. 它是否自然地与 async 同样 WhenAll 基于任务的解决方案
    2. 它还可以插入到更大的任务管道中
      • 您可以通过管道将错误返回来重试错误。
    3. 你可以限制 MaxDegreeOfParallelism 如果节流是一个问题
        5
  •  0
  •   Ian Mercer    5 年前

    Azure Function

    您可以使用Http触发器或服务总线触发器来启动每个下载、处理和上载任务。