C# 我是否可以在后台运行多个慢速进程,以便多个任务可以并行运行?
我在Core.NET2.2框架的顶部编写了一个控制台应用程序,使用的是C# 我是否可以在后台运行多个慢速进程,以便多个任务可以并行运行?,c#,multithreading,asynchronous,async-await,task-parallel-library,C#,Multithreading,Asynchronous,Async Await,Task Parallel Library,我在Core.NET2.2框架的顶部编写了一个控制台应用程序,使用的是C 我的应用程序允许我使用Windows任务调度器触发长时间运行的管理作业 其中一个管理作业进行web API调用,在将大量文件上载到Azure Blob存储之前下载这些文件。以下是我的代码完成任务所需执行的逻辑步骤 调用远程API,该API使用Mime消息进行响应,其中每条消息表示一个文件 解析Mime消息并将每条消息转换为MemoryStream创建一个MemoryStream集合 一旦我有了一个包含多个1000+Memo
C
我的应用程序允许我使用Windows任务调度器触发长时间运行的管理作业
其中一个管理作业进行web API调用,在将大量文件上载到Azure Blob存储之前下载这些文件。以下是我的代码完成任务所需执行的逻辑步骤
MemoryStream
创建一个MemoryStream集合MemoryStream
的集合,我想将每个流
写入Azure Blob存储。由于对远程存储的写入速度很慢,我希望可以使用自己的进程或线程执行每个写入迭代。这将允许我同时并行运行1000多个线程,而不必等待每个写操作的结果。每个线程将负责记录写入/上载过程中可能发生的任何错误。任何记录的错误都将使用不同的作业处理,因此我不必担心重试
我的理解是,调用异步写入/上传流的代码正好可以做到这一点。换句话说,我会说“有一个流
执行它并运行它所需要的时间。只要任务完成,我并不真正关心结果。”
在测试过程中,我发现我对调用async
的理解有些无效。我的印象是,当调用用async
定义的方法时,将在后台线程/工作线程中执行,直到该过程完成。但是,当我测试代码时,我的理解失败了。我的代码告诉我,如果不添加关键字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
}
}
公共异步任务运行()
{
//在调用web API并解析出结果后,将填充此函数
列表文件=新列表{….};
foreach(文件中的流文件)
{
//此代码应该在后台执行,而不必等待结果
等待上传(文件);
}
}
//此方法负责将流上载到存储器并记录错误(如果有)
专用异步任务上载(流)
{
尝试
{
等待存储。创建(文件,GetUniqueName());
}
捕获(例外e)
{
//记录任何错误
}
}
从上述代码中,调用等待上传(文件)代码>正常工作,并将按预期上载文件。但是,由于调用Upload()
方法时使用了wait
,因此在上传代码完成之前,我的循环不会跳转到下一个迭代。同时,删除await
关键字,循环不会等待上载过程,但流从未实际写入存储,就好像我从未调用代码一样
如何并行执行多个Upload
方法,以便每次上传都有一个线程在后台运行?将列表转换为“Upload”任务列表,并使用以下命令等待它们:
公共异步任务运行()
{
//在调用web API并解析出结果后,将填充此函数
列表文件=新列表{….};
var tasks=文件。选择(上载);
等待任务。何时(任务);
}
有关任务/等待的更多信息,请参阅。您可能需要:
var tasks = files.Select(Upload);
await Task.WhenAll(tasks);
请注意,它将产生与文件数量一样多的任务,如果任务太多,可能会导致进程/机器停机。请参见如何解决此问题的示例
我希望我可以使用自己的进程或线程来执行每个写迭代
这并不是最好的方法。进程和线程是有限的资源。您的限制因素是等待网络执行操作
您要做的是:
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);
var tasks=新列表(queue.Count);
而(queue.Count>0)
{
var myobject=Queue.Dequeue();
var task=blockBlob.uploadfromtearrayasync(myobject.content,0,myobject.content.Length);
任务。添加(任务);
}
等待任务。何时(任务);
在这里,我们只是尽可能快地创建任务,然后等待它们全部完成。我们将让.Net框架来处理其余的部分
这里重要的是线程不会提高等待网络资源的速度。任务是一种将需要完成的任务委托给线程的方式,这样您就有更多的线程来做任何事情(比如启动新的上传,或者响应完成的上传)。如果线程只是等待上传完成,那就是浪费资源。其他答案也可以,但是另一种方法是使用Nuget中提供的TPL数据流
公共静态异步任务工作负载(列出结果)
{
var options=新的ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism=50
};
var block=新操作块(MyMethodAsync,选项);
foreach(结果中的var结果)
block.Post(结果);
block.Complete();
等待区块完成;
}
...
公共异步任务MyMethodAsync(结果)
{
//在这里做异步工作
}
数据流的优势
它是否自然地与async一起工作
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);
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
}