每天写,不必定就明白。html
又及,前两天看了一个关于同步方法中调用异步方法的文章,里面有些概念不太正确,因此整理了这个文章。web
先说同步。sql
同步概念你们都很熟悉。在异步概念出来以前,咱们的代码都是按同步的方式写的。简单来讲,就是程序严格按照代码的逻辑次序,一行一行执行。数据库
看一段代码:编程
public static void Main(string[] args)
{
Console.WriteLine("Syc proccess - start");
Console.WriteLine("Syc proccess - enter Func1");
func1();
Console.WriteLine("Syc proccess - out Func1");
Console.WriteLine("Syc proccess - enter Func2");
func2();
Console.WriteLine("Syc proccess - out Func2");
Console.WriteLine("Syc proccess - enter Func3");
func3();
Console.WriteLine("Syc proccess - out Func3");
Console.WriteLine("Syc proccess - done");
}
private static void func1()
{
Console.WriteLine("Func1 proccess - start");
Thread.Sleep(1000);
Console.WriteLine("Func1 proccess - end");
}
private static void func2()
{
Console.WriteLine("Func2 proccess - start");
Thread.Sleep(3000);
Console.WriteLine("Func2 proccess - end");
}
private static void func3()
{
Console.WriteLine("Func3 proccess - start");
Thread.Sleep(5000);
Console.WriteLine("Func3 proccess - end");
}
这是一段简单的一般意义上的代码,程序按代码的次序同步执行,看结果:c#
Syc proccess - start
Syc proccess - enter Func1
Func1 proccess - start
Func1 proccess - end
Syc proccess - out Func1
Syc proccess - enter Func2
Func2 proccess - start
Func2 proccess - end
Syc proccess - out Func2
Syc proccess - enter Func3
Func3 proccess - start
Func3 proccess - end
Syc proccess - out Func3
Syc proccess - done
没有任何意外。微信
为了防止不提供原网址的转载,特在这里加上原文连接:http://www.javashuo.com/article/p-vcglpbog-nd.html网络
那异步呢?多线程
异步,来自于对同步处理的改良和优化。app
应用中,常常会有对于文件或网络、数据库的IO操做。这些操做由于IO软硬件的缘由,须要消耗不少时间,但一般状况下CPU计算量并不大。在同步的代码中,这个过程会被阻塞。直白的说法就是这一行代码没执行完成,程序就得等着,等完成后再执行下一行代码,而这个等待的时间中,CPU资源就被浪费了,闲着了,什么也没作。(固然,操做系统会调度CPU干别的,这儿不抬杠。)
异步编程模型和规范所以出现了,经过某种机制,让程序在等着IO的过程当中,继续作点别的事,等IO的过程完成了,再回来处理IO的内容。这样CPU也没闲着,在等IO的过程当中多作了点事。反映到用户端,就感受程序更快了,用时更短了。
下面重点说一下异步编程相关的内容。
C#中,异步编程,一个核心,两个关键字。
一个核心是指Task
和Task<T>
对象,而两个关键字,就是async
和await
。
从各类渠道给出的异步编程,都是下面的方式:
async Task function()
{
/* your code here */
}
而后调用的方式:
await function();
是这样的吗?嗯,图样图森破~~~
咱们来看代码:
static async Task Main(string[] args)
{
Console.WriteLine("Async proccess - start");
Console.WriteLine("Async proccess - enter Func1");
await func1();
Console.WriteLine("Async proccess - out Func1");
Console.WriteLine("Async proccess - enter Func2");
await func2();
Console.WriteLine("Async proccess - out Func2");
Console.WriteLine("Async proccess - enter Func3");
await func3();
Console.WriteLine("Async proccess - out Func3");
Console.WriteLine("Async proccess - done");
Console.WriteLine("Main proccess - done");
}
private static async Task func1()
{
Console.WriteLine("Func1 proccess - start");
Thread.Sleep(1000);
Console.WriteLine("Func1 proccess - end");
}
private static async Task func2()
{
Console.WriteLine("Func2 proccess - start");
Thread.Sleep(3000);
Console.WriteLine("Func2 proccess - end");
}
private static async Task func3()
{
Console.WriteLine("Func3 proccess - start");
Thread.Sleep(5000);
Console.WriteLine("Func3 proccess - end");
}
跑一下结果:
Async proccess - start
Async proccess - enter Func1
Func1 proccess - start
Func1 proccess - end
Async proccess - out Func1
Async proccess - enter Func2
Func2 proccess - start
Func2 proccess - end
Async proccess - out Func2
Async proccess - enter Func3
Func3 proccess - start
Func3 proccess - end
Async proccess - out Func3
Async proccess - done
Main proccess - done
咦?这个好像跟同步代码的执行结果没什么区别啊?
嗯,彻底正确。上面这个代码,真的是同步执行的。
这是异步编程的第一个容易错误的理解:async
和await
的配对。
在异步编程的规范中,async
修饰的方法,仅仅表示这个方法在内部有可能采用异步的方式执行,CPU在执行这个方法时,会放到一个新的线程中执行。
那这个方法,最终是否采用异步执行,不决定因而否用await
方式调用这个方法,而决定于这个方法内部,是否有await
方式的调用。
看代码,很容易理解:
private static async Task func1()
{
Console.WriteLine("Func1 proccess - start");
Thread.Sleep(1000);
Console.WriteLine("Func1 proccess - end");
}
这个方法,由于方法内部没有await
调用,因此这个方法永远会以同步方式执行,无论你调用这个方法时,有没有await
。
而下面这个代码:
private static async Task func1()
{
Console.WriteLine("Func1 proccess - start");
await Task.Run(() => Thread.Sleep(1000));
Console.WriteLine("Func1 proccess - end");
}
由于这个方法里有await
调用,因此这个方法无论你以什么方式调用,有没有await
,都是异步执行的。
看代码:
static async Task Main(string[] args)
{
Console.WriteLine("Async proccess - start");
Console.WriteLine("Async proccess - enter Func1");
func1();
Console.WriteLine("Async proccess - out Func1");
Console.WriteLine("Async proccess - enter Func2");
func2();
Console.WriteLine("Async proccess - out Func2");
Console.WriteLine("Async proccess - enter Func3");
func3();
Console.WriteLine("Async proccess - out Func3");
Console.WriteLine("Async proccess - done");
Console.WriteLine("Main proccess - done");
Console.ReadKey();
}
private static async Task func1()
{
Console.WriteLine("Func1 proccess - start");
await Task.Run(() => Thread.Sleep(1000));
Console.WriteLine("Func1 proccess - end");
}
private static async Task func2()
{
Console.WriteLine("Func2 proccess - start");
await Task.Run(() => Thread.Sleep(3000));
Console.WriteLine("Func2 proccess - end");
}
private static async Task func3()
{
Console.WriteLine("Func3 proccess - start");
await Task.Run(() => Thread.Sleep(5000));
Console.WriteLine("Func3 proccess - end");
}
输出结果:
Async proccess - start
Async proccess - enter Func1
Func1 proccess - start
Async proccess - out Func1
Async proccess - enter Func2
Func2 proccess - start
Async proccess - out Func2
Async proccess - enter Func3
Func3 proccess - start
Async proccess - out Func3
Async proccess - done
Main proccess - done
Func1 proccess - end
Func2 proccess - end
Func3 proccess - end
结果中,在长时间运行Thread.Sleep
的时候,跳出去往下执行了,是异步。
又有问题来了:不是说异步调用要用await
吗?
咱们把await
加到调用方法的前边,试一下:
static async Task Main(string[] args)
{
Console.WriteLine("Async proccess - start");
Console.WriteLine("Async proccess - enter Func1");
await func1();
Console.WriteLine("Async proccess - out Func1");
Console.WriteLine("Async proccess - enter Func2");
await func2();
Console.WriteLine("Async proccess - out Func2");
Console.WriteLine("Async proccess - enter Func3");
await func3();
Console.WriteLine("Async proccess - out Func3");
Console.WriteLine("Async proccess - done");
Console.WriteLine("Main proccess - done");
Console.ReadKey();
}
跑一下结果:
Async proccess - start
Async proccess - enter Func1
Func1 proccess - start
Func1 proccess - end
Async proccess - out Func1
Async proccess - enter Func2
Func2 proccess - start
Func2 proccess - end
Async proccess - out Func2
Async proccess - enter Func3
Func3 proccess - start
Func3 proccess - end
Async proccess - out Func3
Async proccess - done
Main proccess - done
嗯?怎么又像是同步了?
对,这是第二个容易错误的理解:await
是什么意思?
提到await
,就得先说说Wait
。
字面意思,Wait
就是等待。
前边说了,异步有一个核心,是Task
。而Task
有一个方法,就是Wait
,写法是Task.Wait()
。因此,不少人把这个Wait
和await
混为一谈,这是错的。
这个问题来自于Task
。C#里,Task
不是专为异步准备的,它表达的是一个线程,是工做在线程池里的一个线程。异步是线程的一种应用,多线程也是线程的一种应用。Wait
,以及Status
、IsCanceled
、IsCompleted
、IsFaulted
等等,是给多线程准备的方法,跟异步没有半毛钱关系。固然你非要在异步中使用多线程的Wait
或其它,从代码编译层面不会出错,但程序会。
尤为,Task.Wait()
是一个同步方法,用于多线程中阻塞等待。
在那个「同步方法中调用异步方法」的文章中,用Task.Wait()
来实现同步方法中调用异步方法,这个用法自己就是错误的。 异步不是多线程,并且在多线程中,多个Task.Wait()
使用也会死锁,也有解决和避免死锁的一整套方式。
再说一遍:Task.Wait()
是一个同步方法,用于多线程中阻塞等待,不是实现同步方法中调用异步方法的实现方式。
说回await
。字面意思,也好像是等待。是真的吗?
并非,await
不彻底是等待的意思。
在异步中,await
表达的意思是:当前线程/方法中,await
引导的方法出结果前,跳出当前线程/方法,从调用当前线程/方法的位置,去执行其它可能执行的线程/方法,并在引导的方法出结果后,把运行点拉回到当前位置继续执行;直到遇到下一个await
,或线程/方法完成返回,跳回去刚才外部最后执行的位置继续执行。
有点绕,仍是看代码:
static async Task Main(string[] args)
{
1 Console.WriteLine("Async proccess - start");
2 Console.WriteLine("Async proccess - enter Func1");
3 func1();
4 Console.WriteLine("Async proccess - out Func1");
5 Console.WriteLine("Async proccess - done");
6 Thread.Sleep(2000);
7 Console.WriteLine("Main proccess - done");
8 Console.ReadKey();
}
private static async Task func1()
{
9 Console.WriteLine("Func1 proccess - start");
10 await Task.Run(() => Thread.Sleep(1000));
11 Console.WriteLine("Func1 proccess - end");
}
这个代码,执行时是这样的:顺序执行一、二、3,进到func1
,执行九、10,到10时,有await
,因此跳出,执行四、五、6。而6是一个长时等待,在等待的过程当中,func1
的10运行完成,运行点跳回10,执行11并结束方法,再回到6等待,结束等待后继续执行七、8结束。
咱们看一下结果:
Async proccess - start
Async proccess - enter Func1
Func1 proccess - start
Async proccess - out Func1
Async proccess - done
Func1 proccess - end
Main proccess - done
映证了这样的次序。
在这个例子中,await
在控制异步的执行次序。那为何要用等待这么个词呢?是由于await
确实有等待结果的含义。
这是await
的第二层意思。
await
确实有等待的含义。等什么?等异步的运行结果。
看代码:
static async Task Main(string[] args)
{
Console.WriteLine("Async proccess - start");
Console.WriteLine("Async proccess - enter Func1");
Task<int> f = func1();
Console.WriteLine("Async proccess - out Func1");
Console.WriteLine("Async proccess - done");
int result = await f;
Console.WriteLine("Main proccess - done");
Console.ReadKey();
}
private static async Task<int> func1()
{
Console.WriteLine("Func1 proccess - start");
await Task.Run(() => Thread.Sleep(1000));
Console.WriteLine("Func1 proccess - end");
return 5;
}
比较一下这段代码和上一节的代码,很容易搞清楚执行过程。
这个代码,完成了这样一个需求:咱们须要使用func1
方法的返回值。咱们能够提早去执行这个方法,而不急于拿到方法的返回值,直到咱们须要使用时,再用await
去获取到这个返回值去使用。
这才是异步对于咱们真正的用处。对于一些耗时的IO或相似的操做,咱们能够提早调用,让程序能够利用执行过程当中的空闲时间来完成这个操做。等到咱们须要这个操做的结果用于后续的执行时,咱们await
这个结果。这时候,若是await
的方法已经执行完成,那咱们能够立刻获得结果;若是没有完成,则程序将继续执行这个方法直到获得结果。
正确的方法只有一个:
func1().GetAwaiter().GetResult();
这其实就是await
的一个变形。
(全文完)
![]() |
微信公众号:老王Plus 扫描二维码,关注我的公众号,能够第一时间获得最新的我的文章和内容推送 本文版权归做者全部,转载请保留此声明和原文连接 |