C# async 和 await 理解

先假设如下场景:

主函数 Main,循环等待用户输入;

计算函数 Cal,耗时计算大量数据;

class Test
{
static int Main(string[] args)
{
while(true)
{
// 等待用户输入
}
} public static int Cal() {
int sum = 0;
for (int i = 0; i < 999; i++)
{
sum = sum + i;
}
Console.WriteLine($"sum={sum}");
return sum;
}
}

为了在Main函数中调用Cal函数,同时Cal函数不阻塞主函数的循环,此时需要考虑增加一个CalAsync函数使Cal函数异步执行。

传统的思维方法  在CalAsync函数中启动一个线程,并在线程中执行Cal函数:

// using System.Threading;
public static CalAsync()
{
Thread td = new Thread(new ThreadStart(Cal));
td.start();
}

这种方法显示地创建了一个线程并启动执行,CalAsync函数本身还是在主线程执行并且无法直接获取Cal函数的结果。

async 和 await 异步编程方法  使用async标记CalAsync函数,并在CalAsync函数中创建任务Task异步执行Cal函数,同时使用await标记获取Cal函数的执行结果:

// using System.Threading.Tasks;
public static aysnc void CalAsync()
{
int result = await Task.Run(new Func<int>(Cal));
// 或使用lambda书写方式
// int result = await Task.Run(() => test());
Console.WriteLine(result);
}

在Main函数中直接调用CalAsync函数,可以发现CalAsync成功调用了Cal函数并在一段时间后输出了结果,同时Main函数并不会被阻塞。

分别在Main、Cal、CalAsync函数中增加代码打印当前线程ID:

class Test
{
static void Main(string[] args)
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"Main1 tid {tid}");
Task<int> t = CalAsync();
Console.WriteLine($"Main after CalAsync");
Console.Read();
} public static int Cal()
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"Cal tid {tid}");
int sum = 0;
for (int i = 0; i < 999; i++)
{
sum = sum + i;
}
Console.WriteLine($"sum={sum}");
return sum;
} public static async Task<int> CalAsync()
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"CalAsync1 tid {tid}");
int result = await Task.Run(new Func<int>(Cal));
tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"CalAsync2 tid {tid}, result={result}");
return result;
}
}

结果如图:

可以看出,在CalAsync函数中,await标记之前,代码在主线程中执行,而await标记之后,代码在子线程中执行。

理解与结论:

  • 在C#中, async标记了一个包含异步执行的函数,通过async标记的函数若在主线程中直接调用,则函数一开始仍在主线程中执行;

  • aysnc标记的函数内部必须包含await标记需要异步执行的函数(根据vs2017编译提示),若当前函数在主线程中直接调用,则await标记前的代码在主线程中执行,await标记后的代码在其异步子线程中执行;

  • async标记的函数返回值必须为void、Task、Task< TResult> 类型,可以理解为async标记的函数返回的是 “空”、“即将执行的任务”、“带结果的即将执行的任务”实例;

  • async标记的函数可以继续往下调用async标记函数,调用形式如下例, 从调用逻辑可以理解为await实际上用来触发所标记的Task任务异步执行,并最后获取异步执行的返回值,从运行过程看该触发应该仅对最终的Task任务有效:

 public static async Task<int> CallCalAsync()
{
string tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"CallCalAsync1 tid {tid}");
int result = await CalAsync();
tid = Thread.CurrentThread.ManagedThreadId.ToString();
Console.WriteLine($"CallCalAsync2 tid {tid}, result={result}");
return result;
}

总结

C#中async与await异步编程,可以理解为:

1. async声明了一个包含异步执行代码的函数,该函数执行时不会阻塞调用线程;

2. await存在于async函数中,声明了一个异步执行入口,程序动态运行时从该入口创建并进入一个异步线程环境,并在该线程执行任务实例及任务实例返回之后的代码;

3. 一个async函数中声明多个await关键字时,程序将代码顺序创建并进入异步子线程执行任务实例及任务实例返回之后的代码直到下一个await声明处, 最后一个await声明之后的代码会在最后一个异步子线程中执行 ;

3. await标记的右侧代码返回或定义了一个任务实例,该实例由需要异步执行的目标耗时函数初始化,并在最终定义处触发异步执行。

最新文章

  1. [PHP源码阅读]count函数
  2. 【Java并发编程实战】-----“J.U.C”:CountDownlatch
  3. imx6 DDR_Stress_Test
  4. Nancy Scripts,CSS文件夹配置
  5. hiho #1318 非法二进制数
  6. 数位dp-POJ-3252-Round Numbers
  7. java开发--struts2 标签库使用
  8. 【自动化测试】Selenium的智能等待
  9. JavaScript高级---组合模式设计
  10. [ACM] hdu 1003 Max Sum(最大子段和模型)
  11. Cordova提供了一组设备相关的API,通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头、麦克风等。
  12. JAVA中使用JSONObject对象必备
  13. Android利用canvas画画板
  14. 在昆明网络SEO的走向站外的优化该何去何从?
  15. Android必知必会-获取View坐标和长宽的时机
  16. TreeView的三种状态,全选,全不选,半选中
  17. MySQL主从复制作用和原理
  18. 洗礼灵魂,修炼python(22)--自定义函数(3)—函数作用域,闭包
  19. 嵌入式开发之精确延时---多线程延时阻塞精度asm(&quot;nop&quot;) nanosleep usleep sleep select
  20. 解读经典面试题for循环console.log

热门文章

  1. C# mvc后台传过来的list 怎么在js使用
  2. HBase在特征工程中的应用
  3. Python3基础 函数 参数 多个参数都有缺省值,需要指定参数进行赋值
  4. Jmeter全局变量设置
  5. Fiddler抓包显示请求时延
  6. snmp oid 和厂商对应关系
  7. redis添加到linux系统服务
  8. Java以UTF-8格式读写及追加写文件示例
  9. windows2008R2下iis7.5中的url重写(urlrewrite)
  10. 【Leetcode_easy】925. Long Pressed Name