[英]Can I run multiple slow processes in the background so more than one task can run in parallel?
我在Core .NET 2.2框架的顶部使用C#
编写了一个控制台应用程序。
我的应用程序允许我使用Windows任务调度程序触发长时间运行的管理作业。
其中一个管理作业进行Web-API调用,在将文件上载到Azure Blob存储之前下载大量文件。 以下是我的代码完成工作所需执行的逻辑步骤
MemoryStream
创建MemoryStream
的集合 一旦我有一个包含多个1000+ MemoryStream
的集合,我想将每个Stream
写入Azure Blob存储。 由于对远程存储的写入速度很慢,我希望我可以使用自己的进程或线程执行每次写入迭代。 这将允许我同时并行运行1000+线程,而不必等待每次写入操作的结果。 每个线程都将负责记录在写入/上载过程中可能发生的任何错误。 任何记录的错误都将使用不同的作业处理,因此我不必担心重试。
我的理解是调用异步写入/上传流的代码就是这样做的。 换句话说,我会说“有一个Stream
执行它并运行它所需的时间。只要任务完成,我就不关心结果。”
在测试时,我发现我对调用async
理解有些无效。 我的印象是,当调用使用async
定义的方法时,将在后台线程/ worker中执行,直到该过程完成。 但是,当我测试代码时,我的理解失败了。 我的代码告诉我,如果不添加关键字await
, async
代码永远不会真正执行。 同时,当添加关键字await
时,代码将等待,直到进程在继续之前完成执行。 换句话说,为我的需要添加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()
方法时使用await
,因此在上传代码完成之前,我的循环不会跳转到下一次迭代。 同时,删除await
关键字,循环不等待上传过程,但Stream实际上从不写入存储,就像我从未调用过代码一样。
如何并行执行多个Upload
方法,以便在后台每次上传一个线程?
将列表转换为“上传”任务列表,并使用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);
}
有关tasks / await的更多信息,请参阅此文章 。
我希望我可以使用自己的进程或线程执行每次写迭代。
这不是真正做到这一点的最好方法。 进程和线程是有限的资源。 您的限制因素是等待网络执行操作。
你想要做的就是:
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框架来处理剩下的事情。
这里重要的是Threads不会提高等待网络资源的速度。 任务是一种从线程手中委派需要完成的工作的方法,因此您可以有更多线程来执行任何操作(例如,启动新上载或响应完成的上载)。 如果线程只是等待上传完成,那就是浪费资源。
你可能需要这个:
var tasks = files.Select(Upload);
await Task.WhenAll(tasks);
请注意,它会产生与文件一样多的任务,如果有太多这些任务,可能会导致进程/机器关闭。 请参阅让一组任务一次只运行X作为n示例如何解决该问题。
其他答案很好,但另一种方法是你的TPL DataFlow在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
}
数据流的优势
WhenAll
基于任务的解决方案一样,它是否自然地与async
一起使用 MaxDegreeOfParallelism
您可以将代码转换为Azure功能 ,并让Azure处理大部分并行操作,横向扩展并上载到Azure Blob存储工作。
您可以使用Http Trigger或Service Bus触发器来启动每个下载,处理和上载任务。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.