C# 如何正确等待异步委托?
在同步世界中,我有一个TryExecute函数来包装try/catch/log逻辑以供重用,如下所示:C# 如何正确等待异步委托?,c#,asynchronous,async-await,delegates,C#,Asynchronous,Async Await,Delegates,在同步世界中,我有一个TryExecute函数来包装try/catch/log逻辑以供重用,如下所示: TryExecute(() => SyncFunction()); private static void TryExecute(Action action) { try { action(); } catch (Exception ex) { Log(ex); throw; } } 我不
TryExecute(() => SyncFunction());
private static void TryExecute(Action action)
{
try
{
action();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
我不明白如何将其重写为异步/等待模式
据我所知,我有五种有效的方法将其重写为async/await(忽略任何其他VisualStudio警告)
将原始同步TryExecute()
与异步委托一起使用:
(1) TryExecute(async () => await AsyncFunction());
(2) await TryExecuteTask(() => AsyncFunction());
(3) await TryExecuteTask(async () => await AsyncFunction());
private static Task TryExecuteTask(Func<Task> asyncAction)
{
try
{
return asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
(4) await TryExecuteAsync(() => AsyncFunction());
(5) await TryExecuteAsync(async () => await AsyncFunction());
private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
它似乎不再等待了,TryExecute()
在不等待AsyncFunction()
完成的情况下通过
重写到新的syncTryExecuteTask()
返回任务,使用或不使用异步委托调用该任务:
(1) TryExecute(async () => await AsyncFunction());
(2) await TryExecuteTask(() => AsyncFunction());
(3) await TryExecuteTask(async () => await AsyncFunction());
private static Task TryExecuteTask(Func<Task> asyncAction)
{
try
{
return asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
(4) await TryExecuteAsync(() => AsyncFunction());
(5) await TryExecuteAsync(async () => await AsyncFunction());
private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
但是如果我从AsyncFunction()
内部抛出Exception
,那么上述五种方法都无法捕获异常。所有已停止,出现未处理的异常。只有不带委托的catch工作:
(0) try
{
await AsyncFunction();
}
catch (Exception ex)
{
Log(ex);
}
这意味着我不能使用从(1)到(5)的任何形式的TryExecute()
来重用try/catch/log逻辑,我只能像(0)一样到处重复try/catch/log
我的整个控制台代码如下:
class Program
{
async static Task Main(string[] args)
{
// Original sync way
TryExecute(() => SyncFunction());
Console.WriteLine("0");
try
{
await AsyncFunction();
}
catch (Exception ex)
{
Log(ex);
}
////Console.WriteLine("1");
////TryExecute(async () => await AsyncFunction());
////Console.WriteLine("2");
////await TryExecuteTask(() => AsyncFunction());
////Console.WriteLine("3");
////await TryExecuteTask(async () => await AsyncFunction());
////Console.WriteLine("4");
////await TryExecuteAsync(() => AsyncFunction());
////Console.WriteLine("5");
////await TryExecuteAsync(async () => await AsyncFunction());
Console.WriteLine("Finished without unhandled exception.");
}
private static void SyncFunction()
{
Console.WriteLine("SyncFunction starting");
Thread.Sleep(500);
Console.WriteLine("SyncFunction starting");
throw new Exception();
}
private async static Task AsyncFunction()
{
Console.WriteLine("AsyncFunction starting");
await Task.Run(() =>
{
Console.WriteLine("Sleep starting");
Thread.Sleep(500);
Console.WriteLine("Sleep end");
throw new Exception();
});
Console.WriteLine("AsyncFunction end");
}
private static void TryExecute(Action action)
{
try
{
action();
}
catch (Exception ex)
{
Log(ex);
}
}
private static Task TryExecuteTask(Func<Task> asyncAction)
{
try
{
return asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
private static void Log(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
类程序
{
异步静态任务主(字符串[]args)
{
//原始同步方式
TryExecute(()=>SyncFunction());
控制台写入线(“0”);
尝试
{
等待异步函数();
}
捕获(例外情况除外)
{
对数(ex);
}
////控制台。写入线(“1”);
////TryExecute(异步()=>等待异步函数());
////控制台。写入线(“2”);
////等待TryExecuteTask(()=>AsyncFunction());
////控制台。写入线(“3”);
////wait-TryExecuteTask(async()=>wait-AsyncFunction());
////控制台。写入线(“4”);
////等待TryExecuteAsync(()=>AsyncFunction());
////控制台。写入线(“5”);
////await-TryExecuteAsync(async()=>await-AsyncFunction());
WriteLine(“完成时没有未处理的异常”);
}
私有静态void SyncFunction()
{
Console.WriteLine(“同步功能启动”);
睡眠(500);
Console.WriteLine(“同步功能启动”);
抛出新异常();
}
专用异步静态任务AsyncFunction()
{
Console.WriteLine(“异步函数启动”);
等待任务。运行(()=>
{
控制台写入线(“睡眠启动”);
睡眠(500);
控制台写入线(“睡眠结束”);
抛出新异常();
});
Console.WriteLine(“异步函数结束”);
}
私有静态无效TryExecute(操作)
{
尝试
{
动作();
}
捕获(例外情况除外)
{
对数(ex);
}
}
专用静态任务TryExecuteTask(Func asyncAction)
{
尝试
{
返回asyncAction();
}
捕获(例外情况除外)
{
对数(ex);
投掷;
}
}
专用异步静态任务TryExecuteAsync(Func asyncAction)
{
尝试
{
等待异步操作();
}
捕获(例外情况除外)
{
对数(ex);
投掷;
}
}
私有静态无效日志(异常ex)
{
控制台写入线(例如消息);
}
}
由于未处理的异常,我只能在Main()
class Program
{
async static Task Main(string[] args)
{
await TryExecuteAsync(AsyncFunction);
Console.WriteLine("Finished without unhandled exception.");
}
private async static Task AsyncFunction()
{
Console.WriteLine("AsyncFunction starting");
await Task.Run(() =>
{
Console.WriteLine("Sleep starting");
Thread.Sleep(3000);
Console.WriteLine("Sleep end");
throw new Exception();
});
Console.WriteLine("AsyncFunction end");
}
private async static Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
private static void Log(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
我不明白如何将其重写为异步/等待模式
转换为async
时,第一步是转换方法调用的内容。在这种情况下,应该首先将委托转换为异步兼容委托
Action
是一个委托,它不接受任何参数,也没有返回值,如void Method()
。不接受参数且没有返回值的异步方法类似于async Task method()
,因此
旁注:与学员打交道时,记住这一点尤为重要
一旦您将委托类型从Action
更改为Func
,您就可以wait
其返回值,这将导致您的TryExecute
方法更改为async Task
,如下所示:
private static async Task TryExecuteAsync(Func<Task> asyncAction)
{
try
{
await asyncAction();
}
catch (Exception ex)
{
Log(ex);
throw;
}
}
private静态异步任务TryExecuteAsync(Func asyncAction)
{
尝试
{
等待异步操作();
}
捕获(例外情况除外)
{
对数(ex);
投掷;
}
}
以上五种方法都不能捕获异常。所有已停止,出现未处理的异常
这实际上只是在调试器中运行代码的副作用。对于异步代码,有时确实会看到“未处理”的异常,而这些异常实际上并没有被处理。这是因为编译器生成的代码正在捕获异常并将其放在任务上,当您的代码等待它时,它将被重新引发,然后您的代码将捕获它。当原始异常被您的代码以外的东西捕获(它被编译器生成的代码捕获)时,调试器会有点惊慌失措,它无法知道这是完全正常的
因此,如果您只是继续通过调试器的“未处理”异常,您将看到它工作正常。那么becoms(2)/(3)呢?但我还是不明白为什么(1)没有等待?我在委托中有await
,没有任何警告,它运行(并在没有等待的情况下通过)。wait-TryExecuteAsync(()=>AsyncFunction())有什么问题?调用此方法时,您希望得到什么结果?如果要捕获在调用TryExecuteAsync
中的Log
后重新引发的异常,应在调用TryExecuteAsync
前后放置一个try/catch
子句。名为的函数通常不会引发异常。如果失败,则返回false。感谢所有答案/评论。我疯了