重温异步的美丽
哭死...
落下太多,重新复习程序设计,复习个异步委托用了俩小时
更哭死...
写了六百字了,误碰了个什么按键,结果“Go Back”了,什么都没了,都没心气儿了按刚才那样写
算了,写还不写好点儿。让我们开始“打酱油”!
刚才举了个打酱油的例子,虽然谁都没看到。说的是妈妈让孩子打酱油的传奇故事,同步时候我们怎么做?妈妈喊孩子:“四辈儿,拿着瓶子去打酱油回来妈妈做菜”,你会怎么描述?
酱油 = 四辈儿.打酱油(酱油瓶子);
好,那我们看看这样的方式是什么样的场景吧,妈妈喊孩子“四辈儿,拿着瓶子去打酱油回来妈妈做菜”,“哦”,四辈儿拎着酱油瓶子就跑出去了... 出去是出去了,你知道后面的情况么,你知道四辈儿什么时候回来吗?碰到卖冰棍儿的了、看到捏糖人儿的了、跟小伙伴儿弹玻璃球儿去了、掉下水道里了...保不齐呗,按刚才的逻辑妈妈从四辈儿出门那一刻起就什么都不干了一直等待四辈儿回来,因为要四辈儿回来才能进行下面的工作。这就是我们平时的同步工作。
异步,就是妈妈立即返回厨房不等那个倒霉孩子,该焖饭焖饭,该煲汤煲汤,四辈儿回来会“叫”妈妈告诉妈妈他回来了,并把酱油交给妈妈“哎,姑娘,做菜啊 ...”(离死不远了)
而这就是我们异步的思维和执行逻辑,从 BeginInvoke 说起吧。
还是打酱油,本身我们要调用“打酱油”这个方法,它存在于四辈儿这个“类”中,好,我们怎么异步?我们不由像执行方法的函数直接调用“打酱油”这个方法,而是通过“委托”去迂回执行。
首先,定义一个签名与欲调用方法相同的委托,也就是定义一个指向这个方法类型的委托(当然,根据委托的定义,所有参数相同的方法委托都能为我们执行),下面用伪代码描述运行过程:
Class 四辈儿
{
public 酱油 打酱油(瓶子)
{
...//去打酱油
}
}
Class 妈妈
{
private delegate 酱油 打酱油的委托(瓶子);
private void 我要炒菜()
{
....//洗、切都准备好了
//此处开始异步调用打酱油方法
打酱油的委托 酱油委托 = new 打酱油的委托(四辈儿.打酱油);
IAsyncResult ar = 酱油委托.BeginInvoke(瓶子,new IAsyncCallback(打回来之后呢),酱油委托 );
}
private void 打回来之后呢(IAsyncResult ar)
{
打酱油的委托 接受结果的委托 = (打酱油的委托)ar.AsyncState;
接受结果的委托.EndInvoke(ar);
//其它自定义工作,如
夸奖四辈儿();
}
}
基本呢,就是这样的过程,说明下就是:开始时,定义一个指向打酱油方法的委托,在要执行的时候初始化一个新的委托,定义一个IAsyncResult接口的实例来接受委托执行后的返回值,也就是:
IAsyncResult ar = 酱油委托.BeginInvoke(瓶子,new IAsyncCallback(打回来之后呢),酱油委托 );
这一句,酱油委托正式执行,每个委托都使用BeginInvoke来异步执行,参数依次为:要调用的方法参数,执行后回调方法。特别解释一句的是,Callback那个东西是指“等我们调用的异步执行完成了要干什么”,我们把“打回来之后呢”作为一个新的Callback作为参数放入 BeginInvoke,意思就是,你打酱油执行完了,去给我到“打回来之后呢”那个方法中去报道,所以“打回来之后呢”里边是一些异步执行完成后的处理工作。
同步会在我们执行调用“打酱油”后立即挂起,而异步会立即返回,也即是如果IAsyncResult ar ... 后面有语句,会立即执行。
可累死我了,等于写了两遍,对自己的脑子来说烦都烦死了,贴一个小代码,完成的是异步执行在窗体的Label1中每隔200毫秒就从startNumber到maxNumber逐个显示:
/// <summary>/// 定义一个指向主函数的委托(使用委托来达到异步执行的目的我猜想?,而不是直接执行)
/// </summary>
/// <param name="startnumber"></param>
/// <param name="maxnumber"></param>
private delegate void AsyncIncreaseNumber(int startnumber, int maxnumber);
/// <summary>
/// 为了在另外的线程上操作Label而创建的委托
/// 只是显示用,并不是程序的主要逻辑
/// </summary>
/// <param name="number"></param>
private delegate void ShowNumberDelegate(int number);
/// <summary>
/// 在真正执行的函数中
/// 1.声明新的委托变量,并使其指向要执行的函数
/// </summary>
/// <param name="startNumber"></param>
/// <param name="maxNumber"></param>
private void AsyncIncreaseDemo(int startNumber,int maxNumber)
{
MessageBox.Show("异步委托开始执行");
AsyncIncreaseNumber asyncIncreaseDelegate = new AsyncIncreaseNumber(IncreaseNumber);
IAsyncResult ar = asyncIncreaseDelegate.BeginInvoke(startNumber, maxNumber, new AsyncCallback(IncreaseFinished), asyncIncreaseDelegate);
button1.Enabled = false;
}
/// <summary>
/// 要完成任务的函数主体
/// 此函数完成从开始数字至最大数字的循环显示并休眠200毫秒
/// </summary>
/// <param name="startNumber">开始的数字</param>
/// <param name="maxNumber">结束的数字</param>
private void IncreaseNumber(int startNumber,int maxNumber)
{
ShowNumberDelegate snDelegate = new ShowNumberDelegate(ShowNumber);
int i = startNumber;
while (i < maxNumber)
{
i++;
label1.Invoke(snDelegate, new object[] { i });
Thread.Sleep(200);
}
}
/// <summary>
/// 在Label上显示我们要的数字
/// </summary>
/// <param name="i"></param>
private void ShowNumber(int i)
{
label1.Text = i.ToString();
}
/// <summary>
/// 异步完成的回调方法
/// </summary>
/// <param name="ar"></param>
private void IncreaseFinished(IAsyncResult ar)
{
AsyncIncreaseNumber asyncIncreaseDelegate = (AsyncIncreaseNumber)ar.AsyncState;
asyncIncreaseDelegate.EndInvoke(ar);
MessageBox.Show("恭喜,异步委托完成并返回!");
}
/// <summary>
/// 点击button1开始执行
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
AsyncIncreaseDemo((int)numericUpDown1.Value, (int)numericUpDown2.Value);
//button1.Enabled = true;
}