C#异步编程详解
作者:东秦男人 发布时间:2023-02-13 13:10:39
前言
本节主要介绍异步编程中Task、Async和Await的基础知识。
什么是异步?
异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程。
异步和多线程
相同点:避免调用线程阻塞,从而提高软件的可响应性。
不同点:
异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少 共享变量的数量),减少了死锁的可能。C#5.0 .NET4.5 以后关键字Async和Await的使用,使得异步编程变得异常简单。
多线程中的处理程序依然是顺序执行,但是多线程的缺点也同样明显,线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量可能造成死锁的出现。
异步应用场景及原理
异步主要应用于IO操作,数据库访问,磁盘操作,Socket访问、HTTP/TCP网络通信
原因:对于IO操作并不需要CPU进行过多的计算,这些数据主要通过磁盘进行处理,如果进行同步通信无法结束,需要创建更多的线程资源,线程的数据上下文频繁的切换也是对资源的浪费,针对IO操作不需要单独的分配一个线程来处理。
举例说明:
操作:服务器接收HTTP请求对数据库进行操作然后返回
同步处理请求的线程会被阻塞,异步处理请求的线程不会阻塞。
任务
在使用任务之前,针对线程的调用大多都用线程池提供的静态方法QueueUserWorkItem,但是这个函数有很多的限制,其中最大的问题就是没有内部机制可以让开发者知道操作在什么时候完成,也没有机制在操作完成时获取返回值,微软为了解决这个问题引入了任务的概念。
首先构造一个Task<TResult>对象,并为TResult传递返回值,开始任务之后等待它并回去结果,示例代码:
static void Main(string[] args)
{
Console.WriteLine("开始进行计算");
// ThreadPool.QueueUserWorkItem(Sum, 10);
Task<int> task = new Task<int>(Sum, 100);
task.Start();
//显示等待获取结果
task.Wait();
//调用Result时,等待返回结果
Console.WriteLine("程序结果为 Sum = {0}",task.Result);
Console.WriteLine("程序结束");
Console.ReadLine();
}
public static int Sum(object i)
{
var sum = 0;
for (var j = 0; j <= (int) i; j++)
{
Console.Write("{0} + ",sum);
sum += j;
}
Console.WriteLine( " = {0}",sum);
return sum;
}
除了wait等待单个任务外,task还提供了等待多个任务,WaitAny和WaitAll,它阻止调用线程,直到数组中所有的Task对象完成。
取消任务
任务的取消同样使用的是.NET Framework的标准取消操作模式,首先需要创建一个CancellationTokenSource对象,然后在函数中加入参数CancellationToken,将CancellationTokenSource的Token传递给方法,然后调用IsCancellationRequested获取是否已经取消该值进行判断。
static void Main(string[] args)
{
Console.WriteLine("开始进行计算");
// ThreadPool.QueueUserWorkItem(Sum, 10);
var ctx = new CancellationTokenSource();
var task = new Task<int>(() => Sum(ctx.Token, 100000));
task.Start();
//显示等待获取结果
//task.Wait(ctx.Token);
Thread.Sleep(1000);
ctx.Cancel();
//调用Result时,等待返回结果
Console.WriteLine("程序结果为 Sum = {0}", task.Result);
Console.WriteLine("程序结束");
Console.ReadLine();
}
public static int Sum(CancellationToken cts, object i)
{
var sum = 0;
for (var j = 0; j <= (int)i; j++)
{
if (cts.IsCancellationRequested) return sum;
Thread.Sleep(50);
Console.Write("{0} + ", sum);
sum += j;
}
Console.WriteLine(" = {0}", sum);
return sum;
}
任务完成后自动启动新任务
实际的开发应用中,经常出现一次任务完成后立刻启动另外一个任务,并且不能够使线程阻塞,在任务尚未完成时调用result会使程序阻塞,无法查看任务的执行进度,TASK提供了一个方法ContinueWith,它不会阻塞任何线程,当第一个任务完成时,会立即启动第二个任务。
static void Main(string[] args)
{
Console.WriteLine("开始进行计算");
// ThreadPool.QueueUserWorkItem(Sum, 10);
var ctx = new CancellationTokenSource();
var task = new Task<int>(() => Sum(ctx.Token, 100000));
task.Start();
var cwt = task.ContinueWith(p =>
{
Console.WriteLine("task result ={0} ",task.Result);
});
//显示等待获取结果
//task.Wait(ctx.Token);
Thread.Sleep(1000);
ctx.Cancel();
//调用Result时,等待返回结果
Console.WriteLine("程序结果为 Sum = {0}", task.Result);
Console.WriteLine("程序结束");
Console.ReadLine();
}
public static int Sum(CancellationToken cts, object i)
{
var sum = 0;
for (var j = 0; j <= (int)i; j++)
{
if (cts.IsCancellationRequested) return sum;
Thread.Sleep(50);
Console.Write("{0} + ", sum);
sum += j;
}
Console.WriteLine(" = {0}", sum);
return sum;
}
Async&Await 简单使用
使用Async&Await的主要目的是方便进行异步操作,因为.net 4.0 以前进行异步操作时比较复杂的,主要是通过调用微软提供的异步回调方法进行编程,如果遇到需要自己实现的方法显得非常头疼,.net的各个版本都有自己主推的技术,像.NET1.1中的委托,.NET2.0中的泛型,.NET3.0中的Linq,.NET4.0中的Dynimac,.net4.5主推的就是异步编程,大家只需要了解TASK+异步函数就可以实现异步编程。
async:告诉CLR这是一个异步函数。
await:将Task<TResult>返回值的函数进行异步处理。
示例目的:获取网址JS代码,并在界面显示。
private static async Task<string> DownloadStringWithRetries(string uri)
{
using (var client = new HttpClient())
{
// 第1 次重试前等1 秒,第2 次等2 秒,第3 次等4 秒。
var nextDelay = TimeSpan.FromSeconds(1);
for (int i = 0; i != 3; ++i)
{
try
{
return await client.GetStringAsync(uri);
}
catch
{
}
await Task.Delay(nextDelay);
nextDelay = nextDelay + nextDelay;
}
// 最后重试一次,以便让调用者知道出错信息。
return await client.GetStringAsync(uri);
}
}
static void Main(string[] args)
{
Console.WriteLine("获取百度数据");
ExecuteAsync();
Console.WriteLine("线程结束");
Console.ReadLine();
}
public static async void ExecuteAsync()
{
string text = await DownloadStringWithRetries("http://wwww.baidu.com");
Console.WriteLine(text);
}
运行结果发现,首先获取百度数据,线程结束,最后显示HTML代码,这是因为异步开启了新的线程,并不会造成线程阻塞。
来源:http://www.cnblogs.com/dongqinnanren/p/6278964.html


猜你喜欢
- 现如今,验证码在Android的客户端还是非常普遍的.通过手机账号和验证码直接去注册应用账户的信息.很多应用都以这种方式来完成注
- 本文为大家分享了Android Studio3安装图文教程,供大家参考,具体内容如下Android Studio及其相关资源下载地址:dow
- 1、对称二叉树【OJ链接】分为以下几种情况:二叉树为空,是对称二叉树二叉树不为空,其左子树或者右子树为空,不是对称二叉树二叉树不为空,左右子
- 前言平时开发经常会用到List等集合操作,在这里做一个小结java集合Collectionjava里面集合分为两大类:List和Set,下面
- 本节讲运行在后台服务里的工作请求,如何向发送请求者报告状态。推荐用LocalBroadcastManager发送和接收状态,它限制了只有本a
- Java 常量池的实例详解Java的常量池中包含了类、接口、方法、字符串等一系列常量值。常量池在编译期间就已经确定,并保存在*.class文
- @RequestBody搭配@Data的坑如果用@Data修饰实体类,里面的属性最好不要用连续几个相同字母,如果用千万别用大写。比如下面这个
- C#的FileInfo类提供了与File类相同的功能,不同的是FileInfo提供的都是成员方法,使用示例如下所示:1、读文件://创建只读
- 最近这段时间一直在看Android,利用Listview去实现点赞功能,下面给大家介绍下基本思路。基本思路:进入界面–》获取数据–》 在Li
- 导入表格的主要思路就是:首先从前端页面上传文件,这里先区分一下Multipartfile和File,前者代表的是HTML中form data
- start方法和run方法$start()$方法用来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到$cpu$时间片,就
- 今天介绍一下,我在项目开发过程中,实现状态栏和虚拟按键背景颜色变化的方法,实现方式是,通过隐藏系统的状态栏和虚拟按键的背景,实现图片和背景显
- 题目:使用栈计算类似表达式:5+2*3-2 的计算结果 提示:简易计算器操作符号限于+,-,*,/的计算分析思路:1、
- 背景在我们实际生产容器化部署过程中,往往会遇到 Docker 镜像很大,部署发布很慢的情况影响 docker 镜像大小的因素,主要有以下三个
- 写在前面的废话 下载文件,几乎是所有APP都会用到的功能!算了,还是不废话了,直接开写吧。。。 简单使用 完成
- 在Java中,线程有5中不同状态,分别是:新建(New)、就绪(Runable)、运行(Running)、阻塞(Blocked)和死亡(De
- 本文实例为大家分享了C#实现飞行棋游戏的具体代码,供大家参考,具体内容如下游戏截图:管理类:using System;using Syste
- Android开发中的图片存储本来就是比较耗时耗地的事情,而使用第三方的七牛云,便可以很好的解决这些后顾之忧,最近我也是在学习七牛的SDK,
- 回文数判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。示例 1:输入: 121输出: true示例&
- Ribbon 介绍Ribbon 是 Netflix 提供的一个基于 Http 和 TCP 的客户端负载均衡工具,且已集成在 Eureka 依