既然说到异步编程那就说下异步编程和同步编程的区别。 同步:简单来说就是按顺序执行,例如登录过程必须输入用户名、密码再点击登录 第一步:输入用户名 第二步:输入密码 第三部:点击登录 这就是一个同步过程 异步:异步可以说是同时进行多个任务,相互不干扰,第二个任务的执行不需要等待第一个任务执行。 例如: 下载一个Oracle的安装包,安装过得人应该知道 Oracle的安装包一般是有两个构成,必须两个都下载然后解压在一起才可以开始安装。这里我们下载的过程肯定是不需要先下载安装包1再下载安装包2,而是一起下载,等两个都下载好了进行安装。 我们可以对比下异步和同步所需时间,还是以下载Oracle安装包为例。 假设下载安装包1需要6s,下载安装包2需要4s 同步的操作: 一.下载安装包1 二.下载安装包2 所需时间:6+4 =10s 异步的操作:同时下载安装包1安装包2(排除网络原因) 所需时间应算最长下载时间,所需时间:6s
下面我们直接用代码来呈现异步过程,用一个小demo来实现。
public partial class Form1 : Form
{
//异步编程:基于委托实现
public Form1()
{
InitializeComponent();
}
//[1]定义一个委托
public delegate int MyCalulator(int num);
//[2]根据委托实现方法
private int ExecuteTask1(int num)
{
System.Threading.Thread.Sleep(5000);
return num * num;
}
private int ExecuteTask2(int num)
{
return num * num;
}
//同步调用
private void button1_Click(object sender, EventArgs e)
{
this.label1.Text = ExecuteTask1(10).ToString();
this.label2.Text = ExecuteTask2(10).ToString();
}
//[3]异步调用
private void button2_Click(object sender, EventArgs e)
{
MyCalulator objMycal = ExecuteTask1;//定义委托变量,并引用对应方法
//1.异步调用任务 实际调用参数 回调函数 回调函数入参
IAsyncResult result =objMycal.BeginInvoke(10,null,null);
this.label1.Text = "正在计算请稍等。。。";
//2.并行执行其他任务
this.label2.Text = ExecuteTask2(200).ToString();
//3.获取异步执行结果
int r = objMycal.EndInvoke(result);
this.label1.Text = r.ToString();
}
}
异步是基于委托实现的所以我们第一步需要定义一个委托
public delegate int MyCalulator(int num);
第二步根据委托实现方法
private int ExecuteTask1(int num)
{
System.Threading.Thread.Sleep(5000);
return num * num;
}
第三部编写异步方法
private void button2_Click(object sender, EventArgs e)
{
//通过委托调用方法ExecutrTask1
MyCalulator objMycal = ExecuteTask1;//定义委托变量,并引用对应方法
//1.异步调用任务 实际调用参数 回调函数 回调函数入参
IAsyncResult result =objMycal.BeginInvoke(10,null,null);
this.label1.Text = "正在计算请稍等。。。";
//2.并行执行其他任务
this.label2.Text = ExecuteTask2(200).ToString();
//3.获取异步执行结果
int r = objMycal.EndInvoke(result);
this.label1.Text = r.ToString();
}
编写异步方法的流程在注释里都写出来了,下来重点介绍下其中的几个方法 BeginInvoke函数
IAsyncResult result =objMycal.BeginInvoke(10,null,null);
委托类型的BeginInvoke(<输入和输出变量>,AsyncCallBack callback , object ayncState)方法 :异步调用的核心 第一个参数 表示委托对应的方法实参 第二个参数 回调函数,表示异步调用结束后,自动调用的方法 第三个参数 用于向回到函数提供相关的参数信息 返回值:IAsyncResult->异步操作状态接口,封装了异步执行中的参数
在这里我们只是实现一个简单的异步,并没有使用回调方法,而是直接取了返回值。在后面的文章中会使用回调方法。 下面我们可以看下demo的运行情况
可以看到单机同步后因为第一个计算方法需求等待,所以第二个结果基本是与第一个结果同时出现的,而使用异步结果2直接被计算了出来,之后结果1经过等待时间后被计算。
还是用一个demo来讲解异步方法使用回调函数,先看下demo的运行效果
这里我把输出放在了控制台上,先看代码吧
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
objMyCal = new MyCalculator(ExecuteTask);
}
//【3】创建委托变量
private delegate int MyCalculator(int num, int ms);
private int ExecuteTask(int num,int ms)
{
System.Threading.Thread.Sleep(ms);
return num * num;
}
//【1】声明委托
MyCalculator objMyCal = null;
private void button1_Click(object sender, EventArgs e)
{
//发布任务
for(int i = 1; i < 11; i++)
{
//开始异步执行
objMyCal.BeginInvoke(10 * i, 1000 * i, MyCallBack,i);
//到最后一个参数i给回调函数的字段AsyncState赋值,如果数据很多可以定义成类或结构
}
}
private void MyCallBack(IAsyncResult result)
{
int res = objMyCal.EndInvoke(result);
//显示异步调用结果:result.AsyncState字段用来封装回调函数自定义参数,object类型
Console.WriteLine($"第{result.AsyncState.ToString()}个计算结果为{res}");
}
}
通过上面的代码可以看到,我们每次给睡眠时间增加1000毫秒,如果不采用异步的话,执行时间应该是1+2+3+…+10=55s.然而我们这里采用异步只需要10s左右,大部分流程与上面相同,有不同之处的是这里
objMyCal.BeginInvoke(10 * i, 1000 * i, MyCallBack,i);
这里的第三个参数位置我们添加了回调方法MyCallBack
private void MyCallBack(IAsyncResult result)
{
int res = objMyCal.EndInvoke(result);
//显示异步调用结果:result.AsyncState字段用来封装回调函数自定义参数,object类型
Console.WriteLine($"第{result.AsyncState.ToString()}个计算结果为{res}");
}
在第四个参数位置输入了给回调函数使用的参数,这里的类型是object型,可以传入任何类型的参数,这个参数传入后会保存在IAsyncResult的AsyncState中,在回到函数中我们也进行了调用的示范,在如下这句话中result.AsyncState.ToString()
Console.WriteLine($"第{result.AsyncState.ToString()}个计算结果为{res}");
这段代码就是使用了回调函数的异步调用。
1.异步编程是建立在委托的基础上的一种编程的方法 2.异步调用的每个方法都是独立的线程中执行的。因此,本质上就是一种多线程程序,是简化的多线程 3.比较适合在后头运行较为耗时的《简单任务》,并且任务之间要求相互独立,任务中不应该有直接访问可视化控件的代码 3.如果后台任务要求必须按照特定的顺序执行,或者访问共享资源,则异步编程不太适合,应该选择多线程开发技术。