我阅读了这篇文章,并试图做同样的https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
我正在遍历ODATA中的一组实体,并尝试同时处理所有实体(功能Load())。 我的控制台应用程序看起来像这样
public static async Task Main(string[] args)
{
//List<string> dataareaidlist = new List<string> { "cvl", "cpm", "hk", "ivp",
// "jp", "kr", "me", "na", "sh", "sp", "spb", "spec", "spfr", "spit", "spiv",
// "spuk", "tw", "vgh", "wec", "wge" };
List<string> dataareaidlist = new List<string> { "CAIN", "CPM", "dat", "CGV",
"HK", "JP", "MAL", "ME", "NA", "CVL", "SIN", "SPFR", "SP", "SPIV", "SPIT",
"SPUK", "SPB", "THA", "WEC", "WGE", "TWH", "TW", "KR", "SPEC", "IVP", "MSHK",
"MSSH", "VGH", "SH" };
System.Console.WriteLine("Start program");
List<Load> Entities = new List<Load>();
DateTime startprogram = DateTime.Now;
Entities.Add(new Load("SalesInvoiceLines", dataareaidlist));
Entities.Add(new Load("SalesOrderLines", dataareaidlist));
List<Task<String>> listTaskLoad = new List<Task<String>>();
foreach (Load load in Entities)
{
Entity entity = new Entity(resourceUrl, load.Entity, connection,
accessToken, load.dataareaidlist);
Task<String> t = entity.Load();
listTaskLoad.Add(t);
}
while (listTaskLoad.Any())
{
Task<String> finished = await Task.WhenAny(listTaskLoad);
System.Console.WriteLine("Entity finished:" + finished.Result.ToString());
listTaskLoad.Remove(finished);
finished = null;
}
}
加载功能异步
public async Task<String> Load()
{
System.Console.WriteLine("Start Load for entity:" + EntityName);
// GetNumberEntities();
AnalyzeOdataLoad();
TruncateStg();
if (this.dataareaidlist != null)
{
Parallel.ForEach(this.dataareaidlist, (datareaid) =>
{
String url = resourceUrl + "/data/" + EntityName +
"?$filter=dataAreaId eq '" + datareaid + "'&cross-company=true";
Extract(url, 0, 0, 0);
});
}
else
{
String url = resourceUrl + "/data/" + EntityName + "?cross-company=true";
Extract(url, 0, 0, 0);
}
while (listTaskWrite.Any())
{
Task<Guid> finished = await Task.WhenAny(listTaskWrite);
System.Console.WriteLine(" Task finished:" + finished.Result.ToString());
listTaskWrite.Remove(finished);
finished = null;
}
return this.EntityName;
}
因此,在主程序中,循环启动函数Load()一次开始(并等待整个Load()函数完成,然后再启动下一个),而不是立即启动所有Load() 。不知道为什么它不能一次全部启动。我在extract函数中使用了相同的登录名来加载db异步,并且工作正常。
希望对本教程有所帮助的任何线索或链接。
欢呼
Vincent
答案 0 :(得分:1)
似乎您将异步与并行混淆了。
异步方法是常规方法:调用它们时,它们与其他方法一样在同一线程上运行。魔术发生在作用于不完整await
的第一个Task
上。此时,await
将返回它本身是不完整的Task
,并在Task
完成后注册其余方法以完成操作。
在您的情况下,当您呼叫entity.Load()
时,entity.Load()
的运行就像直到第一个async
都未标记为await
一样。唯一的await
位于await Task.WhenAny(listTaskWrite)
,但是您没有显示listTaskWrite
的填充方式,因此可能永远不会命中该代码。但是无论如何,await
之前的所有内容都会正常运行,就好像它不是async
方法一样。
您有两种选择。如果要在Extract()
中发出网络请求,则可以更改该代码以使其异步(HttpClient
中的所有方法都是异步的),然后摆脱Parallel.ForEach
。这样,它将启动HTTP请求,并在等待答复时开始处理下一个请求。所有这些都可能只使用一个线程完成,但是您已经用尽了它-它永远不会只是坐在那里等待答复。那是对资源的最佳利用。看起来像这样(假设您将Extract()
修改为异步的,并且您想为此目的使用listTaskWrite
)
if (this.dataareaidlist != null)
{
String url = resourceUrl + "/data/" + EntityName +
"?$filter=dataAreaId eq '" + datareaid + "'&cross-company=true";
listTaskWrite.Add(Extract(url, 0, 0, 0));
}
else
{
String url = resourceUrl + "/data/" + EntityName + "?cross-company=true";
listTaskWrite.Add(Extract(url, 0, 0, 0));
}
还可以启动并行作业(在另一个线程上运行代码)并异步等待。为此,您将使用Task.Run
。例如:
Task<String> t = Task.Run(() => entity.Load());
这将在新线程(“并行”)上运行entity.Load()
,并且您可以使用t
来知道它何时完成。但是,您将必须谨慎使用此类代码。您正在为每个项目启动一个新线程,然后使用Parellel.ForEach
,这将创建更多线程。在ASP.NET中,这是个坏主意,因为ASP.NET具有有限数量的可用线程。但是即使在任何其他类型的应用程序中,这些也是很多启动的线程。您可能会发现这样做的性能(完成时间)更糟。