程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#基礎知識 >> C#異步委托講解

C#異步委托講解

編輯:C#基礎知識
異步委托,恰似一江春水向東流(你了解的異步委托)

   書是書,你是你,照抄文字那是書本的東西,你必須有你自己理解,就算是 有錯誤的認識。下面說下異步委托,個人涉 .net不深,經驗尚淺,如果說的不對,希望大家指出來。
    對於線程,我不想多說了,可以搜索博客園 風塵浪子的  "線程上與線程下",說的相當詳細。本篇博文就是針對:了解一些線程但對異步委托又模糊的朋友。
其實這個異步委托,應該結合 線程Thread來一起討論的,為什麼我們現在對這個異步委托愛不釋手,是因為它的3個原因:
.不影響主線程的執行
.合理的利用ThreadPool線程池的線程
.異步委托本質是調用了線程池的工作者線程,不需要為了新建和注銷線程煩惱,統一由線程池管理。

1.傳統的同步委托(模擬委托的方法執行時間很長)
模擬委托的方法執行很長: Thread.Sleep(TimeSpan.FromSeconds(3));

View Code
//聲明委托
public delegate void DeleMethod(string name);
class Program
{
static void Main(string[] args)
{
DeleMethod delemethod = Speaking;
delemethod("Mr.w");
Console.WriteLine("下面我還要執行方法....");

}

//要被委托調用的方法
static void Speaking(string name)
{
Console.WriteLine("我的名字是:{0}",name);
//利用休眠3秒鐘,模擬委托執行的方法很長
Thread.Sleep(TimeSpan.FromSeconds(3));
Console.WriteLine("委托方法執行完畢!");
}



分析:
.按F11大家都可以自己分析了,Main()是所有程序的開始,該程序調用Speaking()方法會休眠3秒,這樣影響了主線程的下面的方法執行時間。
.從頭到尾,程序都只有一個線程,思考下:可不可以在利用一個子線程去幫我執行我的委托方法,不影響我的主線。
答案是:可以,對於這種情況,我們可以 新建Thread類的線程,或者利用異步委托(線程池的工作者線程),這裡我們不適用新建的線程,因為它不便於管理。

2.初始異步委托

View Code
//聲明委托
public delegate void DeleMethod(string name);
class Program
{
static void Main(string[] args)
{
DeleMethod delemethod = Speaking;
//執行異步委托
delemethod.BeginInvoke("Mr.w",null,null);
//主線程方法
Console.WriteLine("下面我還要執行方法....");

}

//要被委托調用的方法
static void Speaking(string name)
{
Console.WriteLine("我的名字是:{0}",name);
//利用休眠3秒鐘,模擬委托執行的方法很長
Thread.Sleep(TimeSpan.FromSeconds(3));
Console.WriteLine("委托方法執行完畢!");
}
}

 

輸出:
下面我還要執行方法....
我的名字是:Mr.w




分析:
1.怎麼回事?怎麼好像 委托方法沒有執行完畢?
答:線程分為:前台線程和後台線程,默認線程池的都是後台線程,主線程是不會管 後台線程是否執行完畢,就直接退出程序。新建的Thread類的線程默認是前台線程,主線程必須等待他執行完畢再退出AppDomain.
是否是前台或者後台線程取決於:IsBackground屬性。
2.我們利用一個Console.ReadKey();可以讓主線程不退出AppDomain。


3.說一說帶返回值的異步委托
大家都知道,有時候,主線程根本就不關心 委托方法的返回值,所以是否能把 異步委托的返回值 顯示在我的主窗口就更不關心了,但是返回值 也是可以解決很多問題的:
1.上篇博文已經提及了,非void委托方法可以返回  異常信息
2.有些軟件需要 把委托方法的返回值 作為另一個方法的參數來處理

代碼1:
 

View Code
//聲明委托
public delegate string DeleMethod(string name);
class Program
{
static void Main(string[] args)
{
DeleMethod delemethod = Speaking;
//執行異步委托
IAsyncResult result=delemethod.BeginInvoke("Mr.w",null,null);
string s=delemethod.EndInvoke(result);
//主線程方法
Console.WriteLine("下面我還要執行方法....");
//阻止主線程退出程序
Console.ReadKey();
}

//要被委托調用的方法
static string Speaking(string name)
{
//利用休眠3秒鐘,模擬委托執行的方法很長
Thread.Sleep(TimeSpan.FromSeconds(3));
Console.WriteLine("我的名字是:{0}", name);
return "委托方法執行完畢!";
}
}
輸出:
我的名字是:Mr.w
下面我還要執行方法....



注意:
看輸出的結果,按照我們的理論,應該輸出結果是:

下面我還要執行方法....
我的名字是:Mr.w

 

怎麼結果相反了?
答案:
1.因為異步委托  由 BeginInvoke()方法開始,以EndInvoke()結束。
2.異步委托的返回值是由 EndInvoke();返回的。

所以:你如果想要返回值,必須寫EndInvoke();,可是有人要問了,如果在主線程調用EndInvoke()方法,主線程不是要等待 子線程執行完畢之後才 會執行下面的代碼,這和  同步執行有什麼區別?那異步就沒有意義了。

正確!完全正確,但是有沒有 又可以得到異步委托的結果,又不影響主線程的方式。

4.說一說 得到返回值,又不影響主線程的方式

解決這個問題,必須了解下,異步委托的BeginInvoke()方法

1.BeginInvoke()最容易被忘記的是:執行BeginInvoke()方法的委托變量,如上面的:delemethod變量必須是單播委托,也就是說只能綁定一個方法。對於多播委托,請把所有方法遍歷出來在使用異步委托,如何遍歷查詢我的上一篇博文。
2.BeginInvoke()方法有4個參數,前2個是和你的委托方法的參數一致的,後2個參數,分別是AsyncCallback類型和Object類型,其中AsyncCallback是一個委托類型,它用於當異步執行完畢時自動進行調用的方法,最後一個參數Object類型可以通過 AsyncResult類型變量的AsyncState屬性獲得,或者 IAsyncResult類型變量的AsyncState屬性
3.BeginInvoke()方法 返回值是 一個IAsyncResult接口,實質就是AsyncResult的對象。通過AsyncResult的AsyncDelegate獲得delemthod委托對象,然後再在其上調用EndInvoke()方法。

是不是越來越迷糊,結合案例看:
代碼1:

View Code
//聲明委托
public delegate string DeleMethod(string name);
class Program
{
static void Main(string[] args)
{
DeleMethod delemethod = Speaking;
//聲明回調函數
AsyncCallback callback = new AsyncCallback(CallBack);
//執行異步委托

delemethod.BeginInvoke("Mr.w",callback,delemethod);

//主線程方法
Console.WriteLine("下面我還要執行方法....");
//阻止主線程退出程序
Console.ReadKey();
}
//回調方法
static void CallBack(IAsyncResult ar)
{
DeleMethod delemethod = (DeleMethod)ar.AsyncState;//BeginInvoke()方法返回的對象的屬性AsyncState,就是BeginInvoke()方法最後一個Object參數
string s = delemethod.EndInvoke(ar);
Console.WriteLine(s);
}
//要被委托調用的方法
static string Speaking(string name)
{
//利用休眠3秒鐘,模擬委托執行的方法很長
Thread.Sleep(TimeSpan.FromSeconds(3));
Console.WriteLine("我的名字是:{0}", name);
return "委托方法執行完畢!";
}
}
輸出:
下面我還要執行方法....
我的名字是:Mr.w
委托方法執行完畢!



先看結果:結果是符合我們的理論的。
分析:
1.大家都知道異步委托要想有返回值,必須調用EndInvoke()方法,但是用哪個委托變量調用呢?
.BeginInvoke()方法最後一個參數就是 BeginInvoke()返回值的IAsyncResult類型變量的AsyncState,這樣獲取委托變量的對象,如代碼1
.或者通過AsyncResult的AsyncDelegate獲得delemethod委托對象,然後再在其上調用EndInvoke()方法。如代碼2
代碼2:
 

View Code
//聲明委托
public delegate string DeleMethod(string name);
class Program
{
static void Main(string[] args)
{
DeleMethod delemethod = Speaking;
//聲明回調函數
AsyncCallback callback = new AsyncCallback(CallBack);
//執行異步委托

delemethod.BeginInvoke("Mr.w",callback,null);
Console.WriteLine("當前線程ID:" + Thread.CurrentThread.ManagedThreadId.ToString());
//主線程方法
Console.WriteLine("下面我還要執行方法....");

//阻止主線程退出程序
Console.ReadKey();
}
//回調方法
static void CallBack(IAsyncResult ar)
{
Console.WriteLine("當前線程ID:" + Thread.CurrentThread.ManagedThreadId.ToString());
AsyncResult result = (AsyncResult)ar;
DeleMethod delemethod = (DeleMethod)result.AsyncDelegate;//通過AsyncResult的AsyncDelegate獲得delemethod委托對象
string s = delemethod.EndInvoke(ar);
Console.WriteLine(s);
Console.WriteLine("當前線程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());
}
//要被委托調用的方法
static string Speaking(string name)
{

//利用休眠3秒鐘,模擬委托執行的方法很長
Thread.Sleep(TimeSpan.FromSeconds(3));
Console.WriteLine("當前線程ID:" + Thread.CurrentThread.ManagedThreadId.ToString());
Console.WriteLine("我的名字是:{0}", name);
return "委托方法執行完畢!";
}
}
輸出:
當前線程ID:1
下面我還要執行方法....
當前線程ID:3
我的名字是:Mr.w
當前線程ID:3
委托方法執行完畢!
當前線程ID:3




總結:異步委托先介紹到這裡,具體的應用還要看你對線程的理解,有時候同步比異步快。有時候異步比同步塊,由具體情況決定。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved