C#中的异步陷阱
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了C#中的异步陷阱,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3460字,纯文字阅读大概需要5分钟。
内容图文
![C#中的异步陷阱](/upload/InfoBanner/zyjiaocheng/1230/24137a3ff28a44e780ad65ee2286363f.jpg)
本文主要介绍异步编程中,常见的异步陷阱:
1、Async没有异步运行
我们来看下面代码,猜测他是如何打印出下面的三个字符串:
/*
Gotcha #1: Async does not run asynchronously
*/
static
void Main(string[] args)
{
Console.WriteLine("begin" + "【" + DateTime.Now.ToString() + "】");
var child = Gotcha1.WorkThenWait();
Console.WriteLine("started" + "【" + DateTime.Now.ToString() + "】");
child.Wait();
Console.WriteLine("completed" + "【" + DateTime.Now.ToString() + "】");
Console.ReadKey();
}
publicstaticasync Task WorkThenWait()
{
Thread.Sleep(5000);
Console.WriteLine("work" + "【" + DateTime.Now.ToString() + "】");
await Task.Delay(10000);
}
看这段代码,如果你猜想,他会按顺序打印出“begin“,”started”,“work”,“completed”,那样的话,你就错了。这段代码会输出“begin“,“work”,“started”,“completed”。
可以看出来,本段代码本来的猜想是,由于WorkThenWait()方法中包含繁重耗时的任务(这里为Thread.Sleep(5000)),所以打算让他异步执行这个方法,但是,可以看出问题出在await关键字用在了该段繁重耗时任务之后,所以后面的child.Wait()方法只是等待了Task.Delay(10000)的执行。
执行结果如下:
2、忽略结果
我们来看下面代码,猜测他是如何执行的,是否会等待:
/*
Gotcha #2: Ignoring results
*/
static
void Main(string[] args)
{
Gotcha2.Handler();
Console.ReadKey();
}
publicstaticasync Task Handler()
{
Console.WriteLine("Before");
Task.Delay(5000);
Console.WriteLine("After");
}
看这段代码,你是否期待它会打印“Before”,等待5秒之后,再打印“After”?当然,又错了。他会立即打印两条字符串,直接没有任何等待。问题出在,虽然Task.Delay返回了Task返回值,但是我们忘记了使用await关键字去等待直到他完成。
执行结果如下:
3、Async void方法
我们来看下面代码,判断下是否能成功打印出异常信息:
/*
Gotcha #3: Async void methods
*/
static
void Main(string[] args)
{
Gotcha3.CallThrowExceptionAsync();
Console.ReadKey();
}
privatestaticasyncvoid ThrowExceptionAsync()
{
thrownew InvalidOperationException();
}
publicstaticvoid CallThrowExceptionAsync()
{
try
{
ThrowExceptionAsync();
}
catch (Exception)
{
Console.WriteLine("Failed");
}
}
你是否觉得这段代码会打印“Failed”?当然,这个异常没有被捕获到,因为ThrowExceptionAsync方法开始执行并立即返回(这个异常发生在background thread内部)。问题根源在于:对于例如 async void Foo(){...},这种方法,C#编译器将生成一个返回void的方法,然后它会在后台创建一个task并执行。这意味着你无法知道这项工作实际何时发生。
4、Async void lambda函数
如果你将异步lambda函数作为委托传递给某个方法时。在这种情况下,C#编译器将从委托类型推断方法的类型。如果使用Action delegate,则编译器生成async void function(这种后台启动线程工作并返回void)。如果使用Func<Task> delegate,编译器讲生成返回Task的function。
我们来看下面代码,判断这段代码是在5秒之后执行完(等待所有的Task完成sleeping),或者它立即完成了?
/*
Gotcha #4: Async void lambda functions
*/
static
void Main(string[] args)
{
Gotcha4.Test();
Console.WriteLine("ok");
}
publicstaticvoid Test()
{
Parallel.For(0, 10, async i => {
await Task.Delay(5000);
});
}
当然,直接看For无法判断出他是否等待,我们借助小工具看下如下代码的源码:
public
void
TestMethod()
{
Parallel.For(
0, 10, async i => {
await Task.Delay(5000);
});
}
//
AsyncGotchasLib.Class1
public
void
TestMethod()
{
int arg_23_0 = 0;
int arg_23_1 = 10;
Action<int> arg_23_2;
if ((arg_23_2 = Class1.<>c.<>9__0_0) == null)
{
arg_23_2 = (Class1.<>c.<>9__0_0 = new Action<int>(Class1.<>c.<>9.<TestMethod>b__0_0));
}
Parallel.For(arg_23_0, arg_23_1, arg_23_2);
}
可以看到For中,lambda函数最终编译为了Action Delegate,因此这段代码不会等待5秒,会立即执行完成,它的等待会在后台线程执行。
5、嵌套任务
看下面代码,问题是,下面两个print直接会不会等待5秒:
/* Gotcha #5: Nesting of tasks */ static void Main(string[] args) { Gotcha5.Test(); Console.ReadKey(); } publicasyncstaticvoid Test() { Console.WriteLine("Before"); await Task.Factory.StartNew( async () => { await Task.Delay(5000); Console.WriteLine("后台线程等待5秒后"); }); Console.WriteLine("After"); }
同样,相当意外,并没有在两个打印(“Before”,“After”)之间等待。为什么?StratNew 方法接受一个委托,并返回一个Task<T>,其中T表示又delegate返回的类型。在这个方法中,这个delegate返回Task,因此,我们获得的结果为Task<Task>。使用await关键字仅仅等待外部Task的完成(它将立即返回内部task),这代表内部task将被忽略,内部task将在后台线程执行。
原文:http://www.cnblogs.com/SimplePerson/p/7395513.html
内容总结
以上是互联网集市为您收集整理的C#中的异步陷阱全部内容,希望文章能够帮你解决C#中的异步陷阱所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。