我有以下代码,除了 WhenAll 上的延续之外,它似乎运行良好......await Task.WhenAll(syncTasks).继续...在所有四个方法完成之前运行。希望对我在这里做错什么的任何指导。我真的不觉得我了解如何安排复杂的异步功能,并且似乎正在发生的事情支持这一点。这是在Xamarin应用程序中,尽管我认为这并不重要。
private async Task SyncItems()
{
var updateItemOnes = Task.Run(() =>
{
UpdateItemOnesToServer(itemOnesToUpdate).ContinueWith(async (result) => {
if (!result.IsFaulted && !result.IsCanceled)
{
await UpdateItemOnesToLocal(itemOnesToUpdate);
}
});
});
syncTasks.Add(updateItemOnes);
var updateItemTwos = Task.Run(() =>
{
UpdateItemTwosToServer(itemTwosToUpdate).ContinueWith(async (result) => {
if (!result.IsFaulted && !result.IsCanceled)
{
await UpdateItemTwosToLocal(itemTwosToUpdate);
}
});
});
syncTasks.Add(updateItemTwos );
//Show Loading Dialog
await Task.WhenAll(syncTasks).ContinueWith((result) => {
if (!result.IsFaulted && !result.IsCanceled)
{
//Success
}
else
{
//Error
}
//Hide Loading Dialog
});
}
private async Task UpdateItemOnesToServer(IEnumerable<Item> itemOnesToUpdate)
{
try
{
var listofTasks = new List<Task>();
foreach (var item in itemOnesToUpdate)
{
var convertItemOneTask = Task.Run(async () => {
//Convert Image File in Item to Base64 here
});
listofTasks.Add(convertItemOneTask);
}
await Task.WhenAll(listofTasks);
var response = await _apiManager.SaveItemOnes(itemOnesToUpdate);
if (response.IsSuccessStatusCode)
{
//Update ItemOnes for Local Update with Response Values
}
}
catch
{
throw;
}
}
private async Task UpdateItemOnesToLocal(IEnumerable<Item> itemOnesToUpdate)
{
var listOfTasks = new List<Task<bool>>();
foreach (var itemOne in itemOnesToUpdate)
{
listOfTasks.Add(_localService.UpdateItemOne(itemOne));
}
await Task.WhenAll<bool>(listOfTasks);
}
private async Task UpdateItemTwosToServer(IEnumerable<ItemOne> itemTwosToUpdate)
{
try
{
var listofTasks = new List<Task>();
foreach (var item in itemTwosToUpdate)
{
var convertItemTwoTask = Task.Run(async () => {
//Convert Image File in Item to Base64 here
});
listofTasks.Add(convertItemTwoTask);
}
await Task.WhenAll(listofTasks);
var response = await _apiManager.SaveItemTwos(itemTwosToUpdate);
if (response.IsSuccessStatusCode)
{
//Update ItemTwos for Local Update with Response Values
}
}
catch
{
throw;
}
}
private async Task UpdateItemTwosToLocal(IEnumerable<ItemTwo> itemTwosToUpdate)
{
var listOfTasks = new List<Task<bool>>();
foreach (var itemTwo in itemTwosToUpdate)
{
listOfTasks.Add(_localService.UpdateItemTwo(itemTwo));
}
await Task.WhenAll<bool>(listOfTasks);
}
提前感谢任何可以提供一点澄清的人。将不胜感激。
所以这段代码存在一些问题。
-
someTask.ContinueWith(X)
基本上,这是说"一旦完成某个任务,就做X"(还有更多的事情发生,但在这种情况下可以这样想)。但是,如果您等待someTask
这将不包括ContinueWith
部分。所以像这样,Task.WhenAll(syncTasks)
不会等待你的ContinueWith
零件。 -
var updateItemOnes = Task.Run(() => UpdateItemOnesToServer())
包装纸。这里没有等待,因此这将创建一个仅启动UpdateItemOnesToServer
任务的任务。这是立即完成的。
如果您想了解实践中发生的事情,请使用此测试类:
class TestAsyncClass
{
public async Task Run()
{
var tasks = new List<Task>();
Console.WriteLine("starting tasks");
var task1 = Task.Run(() => {
FakeServerCall1().ContinueWith(async (result) =>
{
if (!result.IsFaulted && !result.IsCanceled)
await FakeLocalCall1();
});
});
tasks.Add(task1);
var task2 = Task.Run(() => {
FakeServerCall2().ContinueWith(async (result) =>
{
if (result.IsCompletedSuccessfully)
await FakeLocalCall2();
});
});
tasks.Add(task2);
Console.WriteLine("starting tasks completed");
await Task.WhenAll(tasks);
Console.WriteLine("tasks completed");
}
public async Task<bool> FakeServerCall1()
{
Console.WriteLine("Server1 started");
await Task.Delay(3000);
Console.WriteLine("Server1 completed");
return true;
}
public async Task<bool> FakeServerCall2()
{
Console.WriteLine("Server2 started");
await Task.Delay(2000);
Console.WriteLine("Server2 completed");
return true;
}
public async Task<bool> FakeLocalCall1()
{
Console.WriteLine("Local1 started");
await Task.Delay(1500);
Console.WriteLine("Local1 completed");
return true;
}
public async Task<bool> FakeLocalCall2()
{
Console.WriteLine("Local2 started");
await Task.Delay(2000);
Console.WriteLine("Local2 completed");
return true;
}
}
您将看到输出如下所示:
- 启动任务
- 启动任务已完成
- 服务器 1 已启动
- 服务器 2 已启动
- 已完成的任务
- 服务器 2 已完成
- 本地 2 已启动
- 服务器 1 已完成
- 本地 1 已启动
- 本地2已完成
- 本地1已完成
请注意,此处在启动两个任务后直接调用"已完成的任务"。
现在,如果我们像这样更改Run
方法,我想我们将获得您正在寻找的功能:
public async Task Run()
{
var tasks = new List<Task>();
Console.WriteLine("starting tasks");
var task1 = Task.Run(async () =>
{
await FakeServerCall1();
await FakeLocalCall1();
});
tasks.Add(task1);
var task2 = Task.Run(async() =>
{
await FakeServerCall2();
await FakeLocalCall2();
});
tasks.Add(task2);
Console.WriteLine("starting tasks completed");
await Task.WhenAll(tasks);
Console.WriteLine("tasks completed");
}
这将输出:
- 启动任务
- 启动任务已完成
- 服务器 1 已启动
- 服务器 2 已启动
- 服务器 2 已完成
- 本地 2 已启动
- 服务器 1 已完成
- 本地 1 已启动
- 本地2已完成
- 本地1已完成
- 已完成的任务
所以我们在这里看到 Local1 总是在 Server1 之后,Local2 总是在Server2 之后,"任务完成"总是在 Local1 和 Local2 之后
。希望这有帮助!
编辑: 从您的评论中,您说您希望看到过程中发生的任何异常。这是您可以使用ContinueWith
的地方(当引发异常时也会触发它:
await Task.WhenAll(tasks).ContinueWith((result) =>
{
if (result.IsFaulted)
{
foreach (var e in result.Exception.InnerExceptions)
{
Console.WriteLine(e);
}
}
});
如果更改以下测试调用:
public async Task<bool> FakeServerCall2()
{
Console.WriteLine("Server2 started");
await Task.Delay(1000);
Console.WriteLine("Crashing Server2");
throw new Exception("Oops server 2 crashed");
}
public async Task<bool> FakeLocalCall1()
{
Console.WriteLine("Local1 started");
await Task.Delay(1500);
Console.WriteLine("crashing local1");
throw new Exception("Oh ohh, local1 crashed");
}
这将是您的输出:
- 启动任务
- 启动任务已完成
- 服务器 1 已启动
- 服务器 2 已启动
- 服务器崩溃2
- 服务器 1 已完成
- 本地 1 已启动
- 本地崩溃1
- 系统异常:哦哦,本地 1 崩溃了 at TestConsoleApp.TestAsyncClass.FakeLocalCall1() in ~\TestConsoleApp\TestConsoleApp\Program.cs:line 67 在 ~\TestConsoleApp\TestConsoleApp\Program.cs:第 17 行中的 TestConsoleApp.TestAsyncClass.b__0_0() 处
- 系统异常:糟糕的服务器 2 崩溃了 at TestConsoleApp.TestAsyncClass.FakeServerCall2() in ~\TestConsoleApp\TestConsoleApp\Program.cs:line 59 在 ~\TestConsoleApp\TestConsoleApp\Program.cs:line 23 中的 TestConsoleApp.TestAsyncClass.b__0_1()
- 已完成的任务