0%

C# 异步编程初体验(Async/Await)

C# 异步编程初体验(Async/Await)

前言

之前一直没搞懂 C# 的 async 到底是怎么玩的,一直以为和 node.js 的差不多,直到昨天问了一下 Elepover,才发现一直以来理解的 async 和 await 是错误的,于是写下一篇文章记录一下自己所想,说一下自己的初步理解,有错误之处敬请大佬指正。

开始

一开始一直认为,只要 C# 的类型是 Task,并且标志了 async 标识符,那么函数就会异步执行。其实不是的,是否异步执行还得取决于 async 的函数内部有无 await 的标识符。

错误的写法

我也不怕丢人,首先先来给大家看一下之前写的错误写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ASyncProject
{
class Program
{
static async Task Main(string[] args)
{
var test1 = TestData1();
var test2 = TestData2();
Console.WriteLine(await test2);
await test1;
}

static async Task TestData1()
{
Console.WriteLine("1");
Thread.Sleep(10000);
Console.WriteLine("test");
}

static async Task<string> TestData2()
{
Console.WriteLine("2");
Thread.Sleep(5000);
return "233";
}
}
}

这里面的输出结果是

1
2
3
4
5
6
1
<10 seconds later>
test
2
<5 seconds later>
233

我预想的结果是

1
2
3
4
5
6
1
2
<5 seconds later>
233
<5 seconds later>
test

这显然不太对,这个不应该是异步方法吗?那这样和我直接写同步方法有什么区别呢?

修改后的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ASyncProject
{
class Program
{
static async Task Main(string[] args)
{
var test1 = TestData1();
var test2 = TestData2();
Console.WriteLine(await test2);
await test1;
}

static async Task TestData1()
{
Console.WriteLine("1");
await Task.Delay(10000);
Console.WriteLine("test");
}

static async Task<string> TestData2()
{
Console.WriteLine("2");
await Task.Delay(5000);
return "233";
}
}
}

这时候的输出结果就和此处一样了,即

1
2
3
4
5
6
1
2
<5 seconds later>
233
<5 seconds later>
test

看到这里,我想不懂的话也大概明白是什么意思了。那么,再来做个试验?

async 中的同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ASyncProject
{
class Program
{
static async Task Main(string[] args)
{
var test1 = TestData1();
var test2 = TestData2();
Console.WriteLine(await test2);
await test1;
}

static async Task TestData1()
{
Console.WriteLine("1");
Thread.Sleep(2000);
await Task.Delay(10000);
Console.WriteLine("test");
}

static async Task<string> TestData2()
{
Console.WriteLine("2");
await Task.Delay(5000);
Thread.Sleep(2000);
return "233";
}
}
}

这次的输出结果为

1
2
3
4
5
6
7
1
<2 seconds later>
2
<7 seconds later>
233
<3 seconds later>
test

从中我们看到,在 var test1 = TestData1(); 的时候,实际上程序是进入了 TestData1 的方法中,并逐语句地往下执行,如果是同步的语句就按照同步的方法来执行。TestData1()先执行等待 2 秒,继续执行下一句,直到。。遇到 async 函数的第一个 await

这时候就类似于开了一个新的线程,或者可以说 TestData1() 让出了他的控制权,交还给 Main(),让 Main()继续执行下一句语句,也就是 TestData2(),但是此时 TestData1() 自己还在执行,也就是说类似于分开了一个分支,TestData1()就乖乖等待着 Task.Delay(10000)完成,Main()继续跑

TestData2()也是像 TestData1()一样逐个执行下一句,直到遇到 await,TestData2()也让出了他的控制权,Main()继续执行下一行,此时 Main()也遇到 await 了

Main()就等待 test2 的结果返回,然后输出到控制台上。

.Wait() ?

刚才试到 async 里面的 await,那么用.Wait()会怎样呢

我们仅对 TestData1()的 await 做修改,改成.Wait()看看会怎样

1
2
3
4
5
6
7
static async Task TestData1()
{
Console.WriteLine("1");
Thread.Sleep(2000);
Task.Delay(10000).Wait();
Console.WriteLine("test");
}

此时结果为

1
2
3
4
1
<12 seconds later>
test
<...>

这时候.Wait()就相当于变成同步执行的了,它并没有遇到它想要的 await,所以它还不能把控制权交给 Main()。此时虽然说 TestData1()也等待了,但 Main()也要跟着它一起等,等到出现那个 await,Main()才能跟 TestData1()分开,不然 Main()只能看着 TestData() 结束它自己。(悲)

后言

这就是我大概理解的 async 和 await 了,如果发现有错误请求大佬指正,果果很希望得到大家的指教 QwQ