当前位置: 首页 > ai >正文

C#异步编程

文章目录

  • 前言
  • 一、异步方法1
  • 二、异步方法2
  • 二、异步方法3
  • 二、异步方法4
  • 二、异步方法5
  • 二、异步方法6
  • 二、异步方法7


前言

异步编程


一、异步方法1

异步方法:用async关键字修饰的方法
1、异步方法的返回值一般是Task,T是真正的返回值类型,Task。惯例:异步方法名字以Async结尾。
2、即使方法没有返回值,也最好把返回值声明为非泛型的Task.

// 返回值声明为void不建议
public async void GetFileCharLenght(string filePath)
{string s = await File.ReadAllTextAsync(filePath);
}
// 返回值声明为非泛型的Task 建议
public async Task GetFileCharLenght(string filePath)
{string s = await File.ReadAllTextAsync(filePath);
}

3、调用泛型方法时,一般在方法前加上await关键字,这样拿到的返回值就是泛型指定的T类型

// 拿到的返回值就是泛型指定的string类型
public async Task<string> GetFileCharLenght(string filePath)
{string s = await File.ReadAllTextAsync(filePath);//方法前加上await关键字return s;
}

4、异步方法的传染性:一个方法种如果有await调用,则这个方法就必须修饰为async

// 一个方法种如果有await调用,则这个方法就必须修饰为async
public async Task Main()
{await GetFileCharLenght(@"C:\a.txt");
}public async Task<string> GetFileCharLenght(string filePath)
{string s = await File.ReadAllTextAsync(filePath);return s;
}

二、异步方法2

如果同样的功能,既有同步方法,又有异步方法,那么首先使用异步方法。,对于不支持的异步方法,可以使用Wait()(无返回值);Result()(有返回值);风险锁死,尽量不要使用。

// 假如MainWindow不支持异步操作,也就是不能用async关键字修改,
// 而File.ReadAllTextAsync(@"C:\a.txt")这个异步方法有返回值,
// 可以使用Result关键字,将异步方法的返回值取出
public MainWindow()
{InitializeComponent();string s =  File.ReadAllTextAsync(@"C:\a.txt").Result;
}
// 假如Main Window不支持异步操作,而File.ReadAllTextAsync(@"C:\a.txt")这个异步方法有返回值,可以使用Result关键字
public MainWindow()
{InitializeComponent();string s =  File.ReadAllTextAsync(@"C:\a.txt").Result;// 如果没有返回值可以使用Wait等待异步方法执行完毕File.WriteAllTextAsync(@"C:\a.txt", "11111111").Wait();
}

lamda表达式中使用异步方法

// 异步lamda表达式,在lamda表达式中使用async 关键字修饰即可
ThreadPool.QueueUserWorkItem(async (obj) =>
{await File.WriteAllTextAsync(@"C:\a.txt", "11111111");});

二、异步方法3

异步方法不等于多线程,异步方法的代码不会自动在新线程中执行,除非把代码放到新线程中执行。

// 执行前后线程ID不变
static async Task Main()
{// To customize application configuration such as set high DPI settings or default font,// see https://aka.ms/applicationconfiguration.ApplicationConfiguration.Initialize();Application.Run(new Form1());Debug.WriteLine("之前"+Thread.CurrentThread.ManagedThreadId);double r = await CalcAsync(5000);Debug.WriteLine($"r={r}");Debug.WriteLine("之后" + Thread.CurrentThread.ManagedThreadId);
}
public static async Task<double> CalcAsync(int n)
{Debug.WriteLine("CalcAsync"+Thread.CurrentThread.ManagedThreadId);double result = 0;Random rnd = new Random();for (int i = 0; i < n; i++) {result += rnd.NextDouble();}return result;
}
// 结果
/*
之前1
CalcAsync1
r=2495.1055014091776
之后1
*/

除非把代码放到新线程中执行。必须手动将代码放入线程中,也就是使用Task.Run()方法,会自动根据返回值推断出Task.Run()泛型的类型

public static async Task<double> CalcAsync(int n)
{return await Task.Run(() =>{Debug.WriteLine("CalcAsync" + Thread.CurrentThread.ManagedThreadId);double result = 0;Random rnd = new Random();for (int i = 0; i < n; i++){result += rnd.NextDouble();}return result;});
}
// 运行结果
/*
之前1
CalcAsync 5
r=2498.2461521182872
之后5
前后线程id不一样,异步方法执行完后被放到新的线程中执行了
*/

二、异步方法4

如果想要在异步方法中暂停一段时间,不要用Thread.Sleep(),因为它会阻塞调用线程,而要用await Task.Delay()。
例如,在winfrom程序中,如果使用Thread.Sleep()就会阻塞UI线程在睡眠这段时间是无法操作窗体.

private async void button1_Click(object sender, EventArgs e)
{using (HttpClient client = new HttpClient()) {string s1 = await client.GetStringAsync("https://www.youzack.com");textBox1.Text = s1.Substring(0,20);Thread.Sleep(5000);string s2 = await client.GetStringAsync("https://www.baidu.com");textBox1.Text = s2.Substring(0,20);}
}

效果
在这里插入图片描述
使用 await Task.Delay()

private async void button1_Click(object sender, EventArgs e)
{using (HttpClient client = new HttpClient()) {string s1 = await client.GetStringAsync("https://www.youzack.com");textBox1.Text = s1.Substring(0,20);await Task.Delay(5000);string s2 = await client.GetStringAsync("https://www.baidu.com");textBox1.Text = s2.Substring(0,20);}
}

效果
请添加图片描述

二、异步方法5

CancellationToken
有时候需要提前终止任务,比如,请求超时、用户取消请求。很多异步方法都有CancellationToken参数,用于获得提前终止执行的信号。

CancellationToken是一个结构体
有个 None:空的成员
bool IsCancellationRequested 是否发出取消任务的请求
ThrowIfCancellationRequested() 如果任务被取消,执行到这句话就抛异常。

一般不直接使用CancellationToken来获取CancellationToken对象,而是使用CancellationTokenSource来获取CancellationToken对象。CancellationTokenSource中的方法,CancelAfter() 超时后发出取消信号,Cancel()发出取消信号,

private async void button1_Click(object sender, EventArgs e)
{Download1Async("https://www.baidu.com", 100);
}
/// <summary>
/// 未使用CancellationToken,程序会一直执行直到访问改网站100次
/// </summary>
/// <param name="url"></param>
/// <param name="n"></param>
/// <returns></returns>
public async Task Download1Async(string url,int n)
{using (HttpClient client = new HttpClient()){for(var i=0; i < n; i++){string s1 = await client.GetStringAsync(url);textBox1.Text = textBox1.Text + s1.Substring(0, 20);}}
}

使用CancelAfter来终止请求

private async void button1_Click(object sender, EventArgs e)
{CancellationTokenSource source = new CancellationTokenSource();source.CancelAfter(5000);// 5秒之后终止如果请求还未结束,提前终止请求CancellationToken token = source.Token;Download2Async("https://www.baidu.com", 100 , token);
}
public async Task Download2Async(string url, int n,CancellationToken cancellationToken)
{using (HttpClient client = new HttpClient()){for (var i = 0; i < n; i++){string s1 = await client.GetStringAsync(url);textBox1.Text = textBox1.Text + s1.Substring(0, 20);if (cancellationToken.IsCancellationRequested) {textBox1.Clear();textBox1.Text = "取消请求";break;}}}
}

效果 5秒内请求未执行完,请求被提前终止。
请添加图片描述
使用ThrowIfCancellationRequested()来终止请求

private async void button1_Click(object sender, EventArgs e)
{CancellationTokenSource source = new CancellationTokenSource();source.CancelAfter(5000);// 5秒之后终止如果请求还未结束,提前终止请求CancellationToken token = source.Token;Download2Async("https://www.baidu.com", 100 , token);
}
public async Task Download2Async(string url, int n,CancellationToken cancellationToken)
{using (HttpClient client = new HttpClient()){for (var i = 0; i < n; i++){string s1 = await client.GetStringAsync(url);textBox1.Text = textBox1.Text + s1.Substring(0, 20);//if (cancellationToken.IsCancellationRequested) //{//    textBox1.Clear();//    textBox1.Text = "取消请求";//    break;//}cancellationToken.ThrowIfCancellationRequested();}}
}

抛出异常引发的异常:“System.OperationCanceledException”(位于 System.Private.CoreLib.dll 中)

使用GetStringAsync,时传入cancellationToken,让GetStringAsync来处理。

private async void button1_Click(object sender, EventArgs e)
{CancellationTokenSource source = new CancellationTokenSource();source.CancelAfter(5000);// 5秒之后终止如果请求还未结束,提前终止请求CancellationToken token = source.Token;Download3Async("https://www.baidu.com", 100 , token);
}
public async Task Download3Async(string url, int n, CancellationToken cancellationToken)
{using (HttpClient client = new HttpClient()){for (var i = 0; i < n; i++){var req = await client.GetStringAsync(url,cancellationToken);textBox1.Text = textBox1.Text + (await req.Content.ReadAsStringAsync()).Substring(0, 20);}}
}

引发的异常:“System.Threading.Tasks.TaskCanceledException”(位于 System.Private.CoreLib.dll 中)

总结:
在ASP.Net Core开发中,一般不需要自己处理CancellationToken、CancellationTokenSource这些,只要做到,能转发CancellationToken就转发即可

二、异步方法6

WhenAll
Task类的重要方法:
1、Task<Task> WhenAny(IEnumerable<Task> tasks)等,任何一个Task完成,Task就完成
2、Task<TResult[]> WhenAll<TResult>(params <TResult[]> tasks)等,所有Task完成才完成。用于等待多个任务执行结束,但是不在乎它们的执行顺序。
3、FromResult()创建普通数值的Task对象。

Task<TResult[]> WhenAll<TResult>(params <TResult[]> tasks),当3个文档都被读完成才算完成。

public async Task GetAllFileChar()
{Task<string> t1 = File.ReadAllTextAsync(@"C:\a.txt");Task<string> t2 = File.ReadAllTextAsync(@"C:\b.txt");Task<string> t3 = File.ReadAllTextAsync(@"C:\c.txt");string[] strs = await Task.WhenAll(t1, t2, t3);string s1 = strs[0];string s2 = strs[1];string s3 = strs[2];Debug.WriteLine(s1);Debug.WriteLine(s2);Debug.WriteLine(s3);}

二、异步方法7

异步与yield
yield return 不仅能够简化数据的返回,而且可以让数据处理流水线化,提示性能

C#的​​迭代器块(yield return)​​ 实现惰性序列生成。C#的​​迭代器块(yield return)​​ 实现惰性序列生成。

static IEnumerable<string> Test()
{yield return "hello";yield return "hello1";yield return "hello2";
}

在旧版C#中,async方法中不能使用yield,从c#8.0开始,把返回值声明为IAsyncEnumerable(不要带Task),然后遍历的时候用await foreach()即可。

private async void button1_Click(object sender, EventArgs e)
{await foreach(var s in Test1()){textBox1.Text = s;}
}
static async IAsyncEnumerable<string> Test1()
{yield return "hello";yield return "hello1";yield return "hello2";
}
http://www.xdnf.cn/news/19275.html

相关文章:

  • 深度学习量化双雄:PTQ 与 QAT 的技术剖析与实战
  • 异步编程以及promise的一些拓展
  • 【lua】二进制数据打包和解析
  • Nginx反向代理及配置
  • 趣味学RUST基础篇(枚举模式匹配)
  • C语言强化训练(1)
  • Sequelize ORM - 从入门到进阶
  • SIEPIC工具和PDK安装
  • FastAPI 核心实战:精通路径参数、查询参数与数据交互
  • 解决FreeBSD无法使用pkg安装任何程序
  • 入站5年,首创3年,习惯养成4个月,从问题求助者到方案提供者转变,我的CSDN之旅
  • 刚上线的PHP项目被攻击了怎么办
  • 系统架构评估
  • 7.1elementplus的表单
  • Wi-Fi技术——网络安全
  • 代码分析之符号执行技术
  • 鸿蒙Next媒体展示组件实战:Video与动态布局全解析
  • 心路历程-基础命令3
  • 学习笔记:MySQL(day1)
  • 复现 RoboDK 机器人校准功能(以Staubli TX2‑90L / TX200机械臂为测试对象)
  • 腾讯智影AI绘画
  • DriveDreamer4D
  • Qt线程提升:深度指南与最佳实践
  • HTS-AT模型代码分析
  • More Effective C++ 条款17: 考虑使用缓式评估(Consider Using Lazy Evaluation)
  • 快速傅里叶变换FFT推导以及运算复杂度分析
  • 【深入解析——AQS源码】
  • 机器视觉学习-day11-图像噪点消除
  • audioLDM模型代码阅读(二)——HiFi-GAN模型代码分析
  • 对于STM32工程模板