Async与Await 同步与异步操作 - (第三十讲)
转载的B站视频:
源码下载地址,提取码:sa4x
https://pan.baidu.com/s/109_JBTGTjUXVFS4cewEOuA
关于Async与Await的详细介绍
一、什么是异步
同步和异步主要用于修饰方法。当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法;当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调用者不用等待该方法执行完毕,我们称这个方法为异步方法。
异步的好处在于非阻塞(调用线程不会暂停执行去等待子线程完成),因此我们把一些不需要立即使用结果、较耗时的任务设为异步执行,可以提高程序的运行效率。net4.0在ThreadPool的基础上推出了Task类,微软极力推荐使用Task来执行异步任务,现在C#类库中的异步方法基本都用到了Task;net5.0推出了async/await,让异步编程更为方便。本篇主要介绍Task、async/await相关的内容,其他异步操作的方式会在下一篇介绍。
二、Task介绍
Task是在ThreadPool的基础上推出的,我们简单了解下ThreadPool。ThreadPool中有若干数量的线程,如果有任务需要处理时,会从线程池中获取一个空闲的线程来执行任务,任务执行完毕后线程不会销毁,而是被线程池回收以供后续任务使用。当线程池中所有的线程都在忙碌时,又有新任务要处理时,线程池才会新建一个线程来处理该任务,如果线程数量达到设置的最大值,任务会排队,等待其他任务释放线程后再执行。线程池能减少线程的创建,节省开销.
二、async/await介绍
async/await是基于Task的,而Task是对ThreadPool的封装改进,主要是为了更有效的控制线程池中的线程(ThreadPool中的线程,我们很难通过代码控制其执行顺序,任务延续和取消等等);ThreadPool基于Thread的,主要目的是减少Thread创建数量和管理Thread的成本。async/await Task是C#中更先进的,也是微软大力推广的特性,我们在开发中可以尝试使用Task来替代Thread/ThreadPool,处理本地IO和网络IO任务是尽量使用async/await来提高任务执行效率。
Task异步执行,效果演示如下,执行总时间不变,大概是4秒左右。

Task异步执行的代码如下:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp
{
public partial class Form1 : Form
{
private readonly HttpClient httpClient = new HttpClient();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "";
var stopwatch = Stopwatch.StartNew();
DownloadWebsitesSync();
textBox1.Text += $"Elapsed time: {stopwatch.Elapsed}{Environment.NewLine}";
}
private async void button2_Click(object sender, EventArgs e)
{
textBox1.Text = "";
var stopwatch = Stopwatch.StartNew();
await DownloadWebsitesAsync();
textBox1.Text += $"Elapsed time: {stopwatch.Elapsed}{Environment.NewLine}";
}
private void ShowLog(string result)
{
textBox1.Text += result;
}
#region Button1
private void DownloadWebsitesSync()
{
foreach (var site in Contents.WebSites)
{
var result = DownloadWebSiteSync(site);
ShowLog(result);
}
}
private string DownloadWebSiteSync(string url)
{
var response = httpClient.GetAsync(url).GetAwaiter().GetResult();
var responsePayloadBytes = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult();
return $"Finish downloding data from {url}. Total bytes returned {responsePayloadBytes.Length}. {Environment.NewLine}";
}
#endregion
#region Button2
private async Task DownloadWebsitesAsync()
{
foreach (var site in Contents.WebSites)
{
var result = await Task.Run(() => DownloadWebSiteSync(site));
ShowLog(result);
}
}
#endregion
}
}
异步+并行下载的执行效果如下,下载速度提升到了2秒左右,但是启动时第一次耗时还是很大。

异步+并行的代码如下:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp
{
public partial class Form1 : Form
{
private readonly HttpClient httpClient = new HttpClient();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "";
var stopwatch = Stopwatch.StartNew();
DownloadWebsitesSync();
textBox1.Text += $"Elapsed time: {stopwatch.Elapsed}{Environment.NewLine}";
}
private async void button2_Click(object sender, EventArgs e)
{
textBox1.Text = "";
var stopwatch = Stopwatch.StartNew();
await DownloadWebsitesAsync();
textBox1.Text += $"Elapsed time: {stopwatch.Elapsed}{Environment.NewLine}";
}
private void ShowLog(string result)
{
textBox1.Text += result;
}
#region Button1
private void DownloadWebsitesSync()
{
foreach (var site in Contents.WebSites)
{
var result = DownloadWebSiteSync(site);
ShowLog(result);
}
}
private string DownloadWebSiteSync(string url)
{
var response = httpClient.GetAsync(url).GetAwaiter().GetResult();
var responsePayloadBytes = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult();
return $"Finish downloding data from {url}. Total bytes returned {responsePayloadBytes.Length}. {Environment.NewLine}";
}
#endregion
#region Button2
private async Task DownloadWebsitesAsync()
{
// 主要修改点
List<Task<string>> downloadwebTasks = new List<Task<string>>();
foreach (var site in Contents.WebSites)
{
downloadwebTasks.Add(Task.Run(() => DownloadWebSiteSync(site)));
}
var res = await Task.WhenAll(downloadwebTasks);
foreach(var r in res)
{
ShowLog(r);
}
}
#endregion
}
}
进一步优化,解决程序冷启动时的不稳定现象,效果如下:

进一步优化后的代码如下:
#region Button2
private async Task DownloadWebsitesAsync()
{
List<Task<string>> downloadwebTasks = new List<Task<string>>();
foreach (var site in Contents.WebSites)
{
// 修改这个
downloadwebTasks.Add(DownloadWebSiteSync2(site));
}
var res = await Task.WhenAll(downloadwebTasks);
foreach(var r in res)
{
ShowLog(r);
}
}
// 进一步优化,添加这个代码
private async Task<string> DownloadWebSiteSync2(string url)
{
var response = await httpClient.GetAsync(url);
var responsePayloadBytes = await response.Content.ReadAsByteArrayAsync();
return $"Finish downloding data from {url}. Total bytes returned {responsePayloadBytes.Length}. {Environment.NewLine}";
}
#endregion