学习笔记 --- 编码过程中常见的三种异步方式
實際的編碼過程中, 凡是涉及到網絡通信的代碼, 異步都是決不可缺少的. 那么什么是異步呢?
異步就是子線程, 異步通過開辟子線程來實現, 所以一提到異步就應該想到子線程. 即使不涉及網絡通信, 異步也是一種常用的編碼方式, 如: 在Winfor程序中, 某個耗時較長方法我們需要執行10000次, 如果由主線程去循環10000次, 那么客戶的CPU占用率將居高不下, 客戶機器將慢如蝸牛, 甚至整個程序都會出現假死或崩潰的狀態. 作為一名有追求的程序員, 這種情況是絕對不允許的. 其實不光是在Winform程序, 即使是在基于B/S的Web程序中, 異步通信也是很常見的, 比如: Ajax技術. 在這里, 不討論具體的項目環境, 單說3種常見的異步編程(Asynchronous Programming)方式 和 簡要的提下B/S中的異步技術 --- Ajax(參見博客中的<<3種Ajax的實現>>)
?
關于示例的說明: 我們舉個很簡單的例子: 計算1+2+3+...+99+100, 采用最笨的循環方法且每次循環Sleep 100毫秒. 由于窗體程序容易看出效果, 這里在主窗體(MainForm)中放置3個按鈕, 代表3中不同的異步方式. 當點擊按鈕時通過this.Invoke()方法創建子窗體(SubForm), 在第一種異步方式中, 我們是在子線程中創建子窗體的, 如果只是簡單的使用new創建子窗體, 則子窗體會隨著子線程的結束而消亡, 結果就看不到了. 使用this.Invoke()方法可以在子線程中, 由主線程來創建子窗體, 這樣子線程執行完后并不會銷毀主線程創建的窗體. 同時為了將結果顯示在子窗體的textbox中, 需要在子窗體的設計代碼中, 將textbox的修飾符設置為public的. 注意: 第二種異步方式中, 使用new創建了子窗體, 由于是在主線程執行的new, 所以子窗體不會銷毀, 注意體會this.Invoke()的用法.
?
第一種異步: 使用System.Threading.Thread類 + Start()方法.
這種方法需要先New一個Thread類的對象, 在Thread類的構造函數的參數列表中需要再new 一個TheadStart委托類的委托對象(還記得嗎? 委托是用來傳遞方法的), 該委托對象上綁定要執行的處理方法. 最后, 只要在主線程中使用Thread類的對象調用Start()方法即可.
?
第二種異步: 使用委托 + AsyncCallback回調函數.
這種方式首先要定義一個委托類型, 之后要在主線程中創建委托對象, 該委托對象綁定你要執行的方法. 在委托對象上使用BeginInvoke方法開始異步, BeginInvoke方法的參數列表依次是委托對象綁定的方法的參數、new一個AsyncCallback的委托對象, 該對象綁定處理結果的方法(因為一開始就創建了委托對象, 為避免混淆我個人喜歡將這個用于回調的委托稱為回調函數)、委托對象的引用(一開始創建的委托對象).
其實BeginInvoke方法的最后一個參數就是我們一開始創建的委托對象的引用, 保存該引用的目的是為了能夠在子線程中調用一開始創建的委托對象上的EndInvoke方法以便處理結果. 處理結果的子線程方法(或者稱為結果處理方法, 也就是用于回調的委托上綁定的方法)的參數是IAsyncResult, 這個接口類型的參數實際上就是BeginInvoke方法中的最后一個參數, 我們在這個結果處理方法中, 可以通過EndInvoke方法獲得結果并顯示.
?
第三種方式: 使用Async結尾的方法 + 事件處理器.
以上的兩種異步是通用的異步手段, 你可以隨時使用他們實現異步操作. 但第3種借助事件處理器來完成異步操作的方式并不是隨時都可以使用的, 只有類庫提供了Async結尾的方法時, 才可以使用. 我們通過Async結尾的方法來開辟子線程, 當子線程結束時激發一個事件, 我們在這個事件中完成結果處理代碼. 當然, 我們也可以在類中自己實現Async結尾的方法, 記得常委同志還跟我討論過這個問題, 今天就額外寫個實現.
?
第四種方式: Javascript中的異步技術, 也就是傳說中Ajax技術.
具體實例和代碼可以參考博客中的另外一篇文章<<3中Ajax的實現>>, 這里簡單陳述一下. 第一種Ajax技術就是在Javascript中異步獲取結果的方式, 它的實現原理其實就是onreadystatechange事件, 通過該事件上綁定的事件處理器來完成異步. 第二種Ajax技術要稍微復雜一下, 它的原理是借助AjaxPro.dll類庫, 在本地生成一個Javascript中用的代理類, 你可以用這個代理類來完成存取數據的操作. 第3種Ajax技術, 就是微軟在VS2008中提供的Ajax控件, 利用該控件也可以很方便實現Ajax技術, 使用也比較簡單跟著說明做即可.
?
//示例代碼:
//mainform.cs
代碼 using System;using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace AsynchronousProgramming
{
public partial class Mainform : Form
{
public Mainform()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;
}
//#####通過Invoke方法使主線程獲得子線程的控制權#######
private delegate SubForm DelNewForm();
private SubForm createSubForm()
{
SubForm sf = new SubForm();
sf.Show();
return sf;
}
//####################################################
private void button1_Click(object sender, EventArgs e)
{
#region 同步調用
//SubForm sf = new SubForm();
//sf.Show();
//sf.txt_show.Text = sf.Add(1,100).ToString(); //這里為簡單, 將參數硬編碼在這
#endregion
#region Thread類 + Start()方法 實現異步
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(dealProcess));
th.Start();
#endregion
}
private void dealProcess()
{
//SubForm sf = new SubForm(); //直接在子線程中new子窗體, 線程結束子窗體就會關閉, 無法看到結果
SubForm sf = this.Invoke(new DelNewForm(createSubForm)) as SubForm; //第一種異步按鈕彈出的子窗體
sf.Show();
sf.txt_show.Text = sf.Add(1,100).ToString();
}
//####################################################
SubForm subf; //第二種異步彈出的子窗體
delegate int DelAdd(int start, int end);
private void button2_Click(object sender, EventArgs e)
{
#region 委托 + AsyncCallback回調函數 實現異步
//subf = this.Invoke(new DelNewForm(createSubForm)) as SubForm;
subf = new SubForm(); //這里可以直接new SubForm(), 因為第二種異步子窗體不是在子線程中創建的, 不會隨著線程結束消亡
subf.Show();
DelAdd da = new DelAdd(subf.Add);
da.BeginInvoke(1,100,new AsyncCallback(dealAdd), da); //硬編碼參數
#endregion
}
private void dealAdd(IAsyncResult ia)
{
DelAdd da = ia.AsyncState as DelAdd;
subf.txt_show.Text = da.EndInvoke(ia).ToString();
}
//####################################################
SubForm subfm; //第三種異步彈出的子窗體
private void button3_Click(object sender, EventArgs e)
{
#region 使用Async結尾的方法 + 事件處理器 實現異步
//subfm = this.Invoke(new DelNewForm(createSubForm)) as SubForm;
subfm = new SubForm();
subfm.Show();
subfm.AccumulateAsync(1, 100); //我們在SubForm.cs中自定義的Async異步方法(硬編碼參數)
subfm.AccumulateCompleted += new AccumulateCompletedHandler(subfm_AccumulateCompleted); //異步計算完成后觸發的AccumulatedCompleted事件
#endregion
}
void subfm_AccumulateCompleted(object sender, AccumulatedCompletedEventArgs e)
{
subfm.txt_show.Text = e.Result.ToString();
}
}
}
?
//SubForm.cs
代碼 using System;using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace AsynchronousProgramming
{
public partial class SubForm : Form
{
public SubForm()
{
InitializeComponent();
//this.AccumulateCompleted += new AccumulateCompletedHandler(SubForm_AccumulateCompleted); //第三種異步 --- 注冊事件
}
//########################################################
#region 第一種異步(Thread類 + Start()方法) 和 第二種異步(委托 + AsyncCallback回調函數) 調用的方法
public int Add(int start , int end)
{
int sum = 0;
for (int i = start; i <= end; i++)
{
System.Threading.Thread.Sleep(100);
sum += i;
}
return sum;
}
#endregion
//########################################################
#region 第三種異步(Async結尾的方法 + 事件處理器) 調用的方法
//實際計算的方法
public object Accumulate(int start, int end)
{
int result = 0;
for (int i = start; i <= end; i++)
{
System.Threading.Thread.Sleep(100);
result += i;
}
return (object)result;
}
//3. 定義事件, 該事件就是mainform.cs中用來獲取結果的事件
public event AccumulateCompletedHandler AccumulateCompleted;
//4. 注冊事件, 事件的注冊工作通常放在構造函數中, 也可以由事件來注冊. 這里實際的事件注冊過程在mainform.cs中注冊, 事件處理器也放在mainform.cs中
//5. 觸發事件由OnAccumulateCompleted執行
public void OnAccumulateCompleted(object obj)
{
if (AccumulateCompleted != null)
{
this.AccumulateCompleted(this, new AccumulatedCompletedEventArgs(new object[] { obj }));
}
}
private delegate object AccumulateOperation(int start, int end);
//對外提供的AddAsync方法
public void AccumulateAsync(int start, int end) //注意: Async結尾的方法將開辟子線程, 所以返回值為void
{
//此時, 應該異步的調用Accumulate方法, 這里用委托+回調方法來模擬
AccumulateOperation ao = new AccumulateOperation(Accumulate);
ao.BeginInvoke(start, end, new AsyncCallback(dealAccumulate), ao);
}
private void dealAccumulate(IAsyncResult ia)
{
//在子線程中調用Add方法
AccumulateOperation ao = ia.AsyncState as AccumulateOperation;
object result = ao.EndInvoke(ia);
if (result != null)
{
this.OnAccumulateCompleted(result); //結果計算出來后, 觸發事件, 事件處理方法在mainform.cs中
}
}
#endregion
}
//1. 定義累加結果處理方法(事件處理器)的參數對象, 因為我們從該事件處理器中獲得結果信息, 因此參數對象只包含處理結果的信息即可
public class AccumulatedCompletedEventArgs : EventArgs //為了擴大結果的表示范圍, 采用object[]做結果信息類型
{
private object[] _result;
public AccumulatedCompletedEventArgs(object[] result)
{
this._result = result;
}
public int Result
{
get { return (int)this._result[0];}
}
}
//2. 定義委托類型, 該委托類型用來封裝mainform.cs中處理結果的方法(即: AccumulatedCompleted事件的處理器)
public delegate void AccumulateCompletedHandler(object sender, AccumulatedCompletedEventArgs e);
}
?
?
轉載于:https://www.cnblogs.com/cs_net/archive/2011/01/05/1926174.html
總結
以上是生活随笔為你收集整理的学习笔记 --- 编码过程中常见的三种异步方式的全部內容,希望文章能夠幫你解決所遇到的問題。