我想我并没有真正了解c#中的await命令。我认为使用await会允许一个方法继续处理,并且可以在一个单独的线程中工作并在完成后返回一个值但是,我有以下代码:
public async Task<DatabaseActionResult> BackupToAzureAsync(DatabaseSchedule schedule)
{
try
{
foreach (var o in myObjects)
{
await new Task(async () =>
{
try
{
//Do some stuff.
}
catch (Exception e)
{
throw; //Should this throw the exception to the functions Try/Catch block so it can log it to the event viewer?
}
}
}
}
catch (Exception e)
{
//Log exception to event viewer and return a DatabaseActionResult object
}
}
但是,foreach()执行,等待任务完成,然后仅在完成后继续执行下一个任务。如果我删除了await语句,那么它会同时为每个循环迭代运行任务,但Try / Catch块不会将异常抛出堆栈。相反,它只是完全停止服务,就好像它是一个服务未处理的异常。
如何让新任务为每个for / each循环运行一次而不等待前一个循环完成,或者如何让try / catch块将异常抛到堆栈上以便它可以获得在方法的try / catch块中捕获并处理?
谢谢!
后续:
这会工作并仍然保持Try / Catch堆栈吗?
foreach (var t in tables)
{
try
{
var tableTasks = new List<Task>
{
new Task(async () =>
{
try
{
//Do stuff
}
catch (DataError e)
{
throw e;
}
catch (Exception e)
{
throw e;
}
}
)
};
}
catch (Exception e)
{
return new DatabaseActionResult();
}
}
答案 0 :(得分:2)
我想我并不是真的了解c#中的await命令。
这是对的。在之前做一些研究你问一个问题。有很多StackOverflow问题,文章,视频,教程和书籍可以解释这是如何工作的。
我认为使用await会允许方法继续处理,并且可以在单独的线程中工作并在完成后返回值
这是绝对错误的,所以现在不要再考虑所有这些了。</ p>
但是,foreach()执行,等待任务完成,
异步等待 - 等待 - 等待任务完成。这就是为什么它被称为“等待”。
它是异步,因为如果任务未完成,则 await返回给调用者并在此线程上执行其他工作,直到任务完成,此时计划执行await之后的工作。
确保您了解 await是任务的操作员。它不是呼叫上的呼叫约定。任何等待的表达都可以等待;不要陷入相信等待使异步调用的陷阱。被调用的方法已经异步;返回的任务是等待的东西。
如何让每个for / each循环运行一次新任务,而无需等待前一个循环完成
不要等待任务。 等待意味着“在此任务完成之前我无法继续。”
如果您所需的工作流程是“为每个对象创建一个任务并异步等待所有对象完成”,那就是await Task.WhenAll(tasks)
。因此,创建一系列任务 - 使用循环来构建列表,或者Select
来创建序列 - 然后立即等待所有任务。
请注意WhenAll
情况下的异常处理有点不寻常。 在编写代码之前,请仔细阅读文档并了解它。
答案 1 :(得分:0)
我知道其中一些概念花了我一些时间才能解决问题。
这是一个完整的例子(C#7.1),我希望能够解释原帖中给出的问题:
如何让新任务为每个for / each循环运行一次而不等待前一个循环完成,或者如何让try / catch块将异常抛到堆栈上以便它可以获得在方法的try / catch块中捕获并处理?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncTest
{
class Program
{
static async Task Main(string[] args)
{
var data = new List<object>()
{
1, 2, "three", 4, 5
};
// Transform the data into a sequence of Tasks. Note that this does not start these tasks running.
IEnumerable<Task> tasks = data.Select(async (o) =>
{
Console.WriteLine($"Processing: {o}");
await Task.Delay(1000);
if (o is string s) throw new InvalidOperationException($"Cannot perform operation on string '{s}'");
});
try
{
Console.WriteLine("Starting asynchronous processing");
// This call starts all the Tasks in the sequence running. If any of those tasks raises an exception
// the use of the await operator will rethrow that exception here, so we can catch it
await Task.WhenAll(tasks);
Console.WriteLine("All tasks processed successfully");
}
catch (InvalidOperationException e)
{
Console.WriteLine($"Error performing asynchronous work: {e.Message}");
Console.WriteLine("Not all tasks processed successfully");
}
Console.WriteLine("Asynchronous processing finished. Exiting.");
}
}
}