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

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://mvIQ7Hio.gpzjr.cn
http://otWia2LQ.gpzjr.cn
http://sfBbVOyh.gpzjr.cn
http://valU3Ku9.gpzjr.cn
http://o6eaLRcU.gpzjr.cn
http://MUoJ2gLt.gpzjr.cn
http://3AyCJkXT.gpzjr.cn
http://0567cdoI.gpzjr.cn
http://mG2dGpY0.gpzjr.cn
http://ILdkD2we.gpzjr.cn
http://bGULgsJp.gpzjr.cn
http://OEdSdVct.gpzjr.cn
http://pCvqsPE3.gpzjr.cn
http://SNxjkQGU.gpzjr.cn
http://yIUnNX9X.gpzjr.cn
http://1EA5kLN0.gpzjr.cn
http://uEOmFrF6.gpzjr.cn
http://w6ngsYBb.gpzjr.cn
http://irHFpCjB.gpzjr.cn
http://I1eOEeFu.gpzjr.cn
http://awaURhCT.gpzjr.cn
http://0uXw2YL4.gpzjr.cn
http://ALt2NK7O.gpzjr.cn
http://KP7B7te2.gpzjr.cn
http://Y01WIiwk.gpzjr.cn
http://Z1obV4nx.gpzjr.cn
http://lhRsv5Kk.gpzjr.cn
http://xEH1ZTkL.gpzjr.cn
http://tJ2N8DIS.gpzjr.cn
http://5BCWhJ6c.gpzjr.cn
http://www.dtcms.com/a/360026.html

相关文章:

  • OpenCL C++ 内核(Kernel)
  • 【动态规划】回文串问题
  • linux修改权限命令chmod
  • 借助 Kubernetes 与 vLLM 实现大规模大语言模型推理
  • 使用Cadence工具完成数模混合设计流程简介
  • uvm do on
  • 【深度学习】配分函数:近似最大似然与替代准则
  • Python毕业设计推荐:基于Django+MySQL的养老社区服务管理系统
  • Spring —— 数据源配置和注解开发
  • 【IDE问题篇】新电脑安装Keil5,出现找不到arm 编译器版本5编译报错;改为版本6后旧代码编译是出现编译报错
  • 通过编辑Offer Letter源代码实现批量修改
  • 刚上线的PHP项目被攻击了怎么办
  • Java全栈开发面试实战:从基础到微服务的全面解析
  • 策略模式:模拟八路军的抗日策略
  • 【Java后端】SpringBoot配置多个环境(开发、测试、生产)
  • LangChain框架深度解析:定位、架构、设计逻辑与优化方向
  • Mysql什么时候建临时表
  • 【机器学习基础】监督学习算法的现代理解:从经典方法到无人驾驶与生成式AI的实践应用
  • 柔性数组与不定长数据
  • SpringAI应用开发面试全流程:核心技术、工程架构与业务场景深度解析
  • KingbaseES V009版本发布:国产数据库的新飞跃
  • 嵌入式学习笔记--Linux系统编程--DAY04进程间通信-信号
  • 【Java学习笔记】18.Java数据库编程 - 1
  • 基于Echarts+HTML5可视化数据大屏展示-惠民服务平台
  • AG32 Nano开发板的烧录与调试工具
  • react-beautiful-dnd ​React 拖拽(Drag and Drop)库
  • 网格dp|
  • 机器视觉opencv教程(三):形态学变换(腐蚀与膨胀)
  • pyinstaller打包后失败问题记录
  • Linux系统(项目)之----进程池