[转] C#异步操作
Title
通過(guò)委托實(shí)現(xiàn)異步調(diào)用中BeginInvoke及回調(diào)函數(shù)的使用
通過(guò)委托實(shí)現(xiàn)異步調(diào)用的步驟:
1.定義委托。
2.將要進(jìn)行異步調(diào)用的方法“實(shí)例化”到定義的委托。
3.在委托上調(diào)用BeginInvoke方法。其中,BeginInvoke的參數(shù)由三個(gè)部分構(gòu)成。第一部分:所定義的委托的函數(shù)簽名。
第二部分:希望調(diào)用的回調(diào)函數(shù)的委托。第三部分:自定義委托的實(shí)例(該實(shí)例將會(huì)在回調(diào)函數(shù)中的IAsyncResult的AsyncRState屬性中重構(gòu)出我們?cè)诓襟E2中定義的委托實(shí)例,并借助這個(gè)實(shí)例來(lái)調(diào)用EndInvoke方法。)
4.如果我們希望在當(dāng)前線程來(lái)處理異步調(diào)用的結(jié)果,則可以使用BeginInvoke方法返回一個(gè)IAsyncResult實(shí)例(例如ar)
并在當(dāng)前線程等待。如果我們希望在異步線程中通過(guò)回調(diào)函數(shù)來(lái)處理結(jié)果,則我們需要在3中傳遞一個(gè)回調(diào)委托,并在該處理中調(diào)用EndInvoke方法。
以下是一段Programming C#(4版)中的一段實(shí)例:
Codeusing?System;
using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
using?System.Threading;
namespace?property
{
????public?class?DelegateClass
????{
????????public?delegate?int?AsyncSampDelegate();
????????public?event?AsyncSampDelegate?delEvent;
????????public?void?Run()
????????{
????????????Console.WriteLine("The?Run?Thread?is?{0}",?Thread.CurrentThread.GetHashCode());
????????????foreach?(AsyncSampDelegate?del?in?delEvent.GetInvocationList())
????????????{
????????????????del.BeginInvoke(new?AsyncCallback(ReturnAsync),?del);
????????????}
????????}
????????public?void?ReturnAsync(IAsyncResult?ar)
????????{
????????????//獲得調(diào)用委托實(shí)例的引用
????????????AsyncSampDelegate?del?=?(AsyncSampDelegate)ar.AsyncState;
????????????int?result?=?del.EndInvoke(ar);
????????????Console.WriteLine("The?result?is?{0},The?Thread?is?{1}",?result,?Thread.CurrentThread.GetHashCode());
????????}
????}
????public?class?FirstSubscribe
????{
????????private?int?myCount?=?0;
????????public?void?AddFunToDel(DelegateClass?tmpDel)
????????{
????????????tmpDel.delEvent+=new?DelegateClass.AsyncSampDelegate(FirstFun);
????????}
????????public?int?FirstFun()
????????{
????????????return?myCount++;
????????}
????}
????public?class?SecondSubscribe
????{
????????private?int?myCount?=?0;
????????public?void?AddFunToDel(DelegateClass?tmpDel)
????????{
????????????tmpDel.delEvent+=new?DelegateClass.AsyncSampDelegate(SecondFun);
????????}
????????public?int?SecondFun()
????????{
????????????return?myCount?+=?2;
????????}
????}
????public?class?App
????{
????????static?void?Main()
????????{
????????????DelegateClass?delClass?=?new?DelegateClass();
????????????FirstSubscribe?fs?=?new?FirstSubscribe();
????????????SecondSubscribe?ss?=?new?SecondSubscribe();
????????????fs.AddFunToDel(delClass);
????????????ss.AddFunToDel(delClass);
????????????Console.WriteLine("The?Main?Thread?is?{0}",?Thread.CurrentThread.GetHashCode());
????????????delClass.Run();
????????????Console.Read();
????????}
????}
}?
?
?
?
多線程和異步操作的異同
多線程和異步操作兩者都可以達(dá)到避免調(diào)用線程阻塞的目的,從而提高軟件的可響應(yīng)性。甚至有些時(shí)候我們就認(rèn)為多線程和異步操作是等同的概念。但是,多線程和異步操作還是有一些區(qū)別的。而這些區(qū)別造成了使用多線程和異步操作的時(shí)機(jī)的區(qū)別。
異步操作的本質(zhì)
所有的程序最終都會(huì)由計(jì)算機(jī)硬件來(lái)執(zhí)行,所以為了更好的理解異步操作的本質(zhì),我們有必要了解一下它的硬件基礎(chǔ)。 熟悉電腦硬件的朋友肯定對(duì)DMA這個(gè)詞不陌生,硬盤(pán)、光驅(qū)的技術(shù)規(guī)格中都有明確DMA的模式指標(biāo),其實(shí)網(wǎng)卡、聲卡、顯卡也是有DMA功能的。DMA就是直 接內(nèi)存訪問(wèn)的意思,也就是說(shuō),擁有DMA功能的硬件在和內(nèi)存進(jìn)行數(shù)據(jù)交換的時(shí)候可以不消耗CPU資源。只要CPU在發(fā)起數(shù)據(jù)傳輸時(shí)發(fā)送一個(gè)指令,硬件就開(kāi) 始自己和內(nèi)存交換數(shù)據(jù),在傳輸完成之后硬件會(huì)觸發(fā)一個(gè)中斷來(lái)通知操作完成。這些無(wú)須消耗CPU時(shí)間的I/O操作正是異步操作的硬件基礎(chǔ)。所以即使在DOS 這樣的單進(jìn)程(而且無(wú)線程概念)系統(tǒng)中也同樣可以發(fā)起異步的DMA操作。
線程的本質(zhì)
線程不是一個(gè)計(jì)算機(jī)硬件的功能,而是操作系統(tǒng)提供的一種邏輯功能,線程本質(zhì)上是進(jìn)程中一段并發(fā)運(yùn)行的代碼,所以線程需要操作系統(tǒng)投入CPU資源來(lái)運(yùn)行和調(diào)度。
異步操作的優(yōu)缺點(diǎn)
因?yàn)楫惒讲僮鳠o(wú)須額外的線程負(fù)擔(dān),并且使用回調(diào)的方式進(jìn)行處理,在設(shè)計(jì)良好的情況下,處理函數(shù)可以不必使用共享變量(即使無(wú)法完全不用,最起碼可以減少 共享變量的數(shù)量),減少了死鎖的可能。當(dāng)然異步操作也并非完美無(wú)暇。編寫(xiě)異步操作的復(fù)雜程度較高,程序主要使用回調(diào)方式進(jìn)行處理,與普通人的思維方式有些 初入,而且難以調(diào)試。
多線程的優(yōu)缺點(diǎn)
多線程的優(yōu)點(diǎn)很明顯,線程中的處理程序依然是順序執(zhí)行,符合普通人的思維習(xí)慣,所以編程簡(jiǎn)單。但是多線程的缺點(diǎn)也同樣明顯,線程的使用(濫用)會(huì)給系統(tǒng)帶來(lái)上下文切換的額外負(fù)擔(dān)。并且線程間的共享變量可能造成死鎖的出現(xiàn)。
適用范圍
在了解了線程與異步操作各自的優(yōu)缺點(diǎn)之后,我們可以來(lái)探討一下線程和異步的合理用途。我認(rèn)為:當(dāng)需要執(zhí)行I/O操作時(shí),使用異步操作比使用線程+同步 I/O操作更合適。I/O操作不僅包括了直接的文件、網(wǎng)絡(luò)的讀寫(xiě),還包括數(shù)據(jù)庫(kù)操作、Web Service、HttpRequest以及.net Remoting等跨進(jìn)程的調(diào)用。
而線程的適用范圍則是那種需要長(zhǎng)時(shí)間CPU運(yùn)算的場(chǎng)合,例如耗時(shí)較長(zhǎng)的圖形處理和算法執(zhí)行。但是往 往由于使用線程編程的簡(jiǎn)單和符合習(xí)慣,所以很多朋友往往會(huì)使用線程來(lái)執(zhí)行耗時(shí)較長(zhǎng)的I/O操作。這樣在只有少數(shù)幾個(gè)并發(fā)操作的時(shí)候還無(wú)傷大雅,如果需要處 理大量的并發(fā)操作時(shí)就不合適了。
實(shí)例研究
說(shuō)了那么理論上的東西,可能有些兄弟早就不耐煩了,現(xiàn)在我們來(lái)研究幾個(gè)實(shí)際的異步操作例子吧。
實(shí)例1:由delegate產(chǎn)生的異步方法到底是怎么回事?
大家可能都知道,使用delegate可以“自動(dòng)”使一個(gè)方法可以進(jìn)行異步的調(diào)用。從直覺(jué)上來(lái)說(shuō),我覺(jué)得是由編譯器或者CLR使用了另外的線程來(lái)執(zhí)行目標(biāo)方法。到底是不是這樣呢?讓我們來(lái)用一段代碼證明一下吧。
?
Codeusing?System;
using?System.Threading;
namespace?AsyncDelegateDemo
{
??delegate?void?AsyncFoo(int?i);
??class?Program
??{
????///?<summary>
????///?輸出當(dāng)前線程的信息
????///?</summary>
???///?<param?name="name">方法名稱</param>
????static?void?PrintCurrThreadInfo(string?name)
????{
??????Console.WriteLine("Thread?Id?of?"?+?name+?"?is:?"?+?Thread.CurrentThread.ManagedThreadId+?",?current?thread?is?"
??????+?(Thread.CurrentThread.IsThreadPoolThread???""?:?"not?")
??????+?"thread?pool?thread.");
????}
????///?<summary>
????///?測(cè)試方法,Sleep一定時(shí)間
????///?</summary>
????///?<param?name="i">Sleep的時(shí)間</param>
????static?void?Foo(int?i)
????{
???????PrintCurrThreadInfo("Foo()");
???????Thread.Sleep(i);
????}
????///?<summary>
????///?投遞一個(gè)異步調(diào)用
????///?</summary>
????static?void?PostAsync()
????{
??????AsyncFoo?caller?=?new?AsyncFoo(Foo);
??????caller.BeginInvoke(1000,?new?AsyncCallback(FooCallBack),?caller);
????}
????static?void?Main(string[]?args)
????{
??????PrintCurrThreadInfo("Main()");
??????for(int?i?=?0;?i?<?10?;?i++)
??????{
?????????PostAsync();
??????}
??????Console.ReadLine();
????}
????static?void?FooCallBack(IAsyncResult?ar)
????{
??????PrintCurrThreadInfo("FooCallBack()");
??????AsyncFoo?caller?=?(AsyncFoo)?ar.AsyncState;
??????caller.EndInvoke(ar);
????}
??}
}?
這段代碼代碼的輸出如下:
Thread Id of Main() is: 1, current thread is not thread pool thread.
Thread Id of Foo() is: 3, current thread is thread pool thread.
Thread Id of FooCallBack() is: 3, current thread is thread pool thread.
Thread Id of Foo() is: 3, current thread is thread pool thread.
Thread Id of Foo() is: 4, current thread is thread pool thread.
Thread Id of Foo() is: 5, current thread is thread pool thread.
Thread Id of FooCallBack() is: 3, current thread is thread pool thread.
Thread Id of Foo() is: 3,
?
?///
http://www.cnsdn.com.cn/blog/article.asp?id=2164
///
?
。NET?Framework?為異步操作提供了兩種設(shè)計(jì)模式:使用?IAsyncResult?對(duì)象的異步操作與使用事件的異步操作。先來(lái)學(xué)習(xí)前者
概述
IAsyncResult?異步設(shè)計(jì)模式通過(guò)名為?BeginOperationName?和?EndOperationName?的兩個(gè)方法來(lái)實(shí)現(xiàn)原同步方法的異步調(diào)用,如?FileStream?類提供了?BeginRead?和?EndRead?方法來(lái)從文件異步讀取字節(jié),它們是?Read?方法的異步版本
Begin?方法包含同步方法簽名中的任何參數(shù),此外還包含另外兩個(gè)參數(shù):一個(gè)AsyncCallback?委托和一個(gè)用戶定義的狀態(tài)對(duì)象。委托用來(lái)調(diào)用回調(diào)方法,狀態(tài)對(duì)象是用來(lái)向回調(diào)方法傳遞狀態(tài)信息。該方法返回一個(gè)實(shí)現(xiàn)?IAsyncResult?接口的對(duì)象
End?方法用于結(jié)束異步操作并返回結(jié)果,因此包含同步方法簽名中的?ref?和?out?參數(shù),返回值類型也與同步方法相同。該方法還包括一個(gè)?IAsyncResult?參數(shù),用于獲取異步操作是否完成的信息,當(dāng)然在使用時(shí)就必須傳入對(duì)應(yīng)的?Begin?方法返回的對(duì)象實(shí)例
開(kāi)始異步操作后如果要阻止應(yīng)用程序,可以直接調(diào)用?End?方法,這會(huì)阻止應(yīng)用程序直到異步操作完成后再繼續(xù)執(zhí)行。也可以使用?IAsyncResult?的?AsyncWaitHandle?屬性,調(diào)用其中的WaitOne等方法來(lái)阻塞線程。這兩種方法的區(qū)別不大,只是前者必須一直等待而后者可以設(shè)置等待超時(shí)
如果不阻止應(yīng)用程序,則可以通過(guò)輪循?IAsyncResult?的?IsCompleted?狀態(tài)來(lái)判斷操作是否完成,或使用?AsyncCallback?委托來(lái)結(jié)束異步操作。AsyncCallback?委托包含一個(gè)?IAsyncResult?的簽名,回調(diào)方法內(nèi)部再調(diào)用?End?方法來(lái)獲取操作執(zhí)行結(jié)果
嘗試
先來(lái)熟悉一下今天的主角,IAsyncResult?接口
public?interface?IAsyncResult
{
object?AsyncState?{?get;?}
WaitHandle?AsyncWaitHandle?{?get;?}
bool?CompletedSynchronously?{?get;?}
bool?IsCompleted?{?get;?}
}
????????????
?
我用一個(gè)?AsyncDemo?類作為異步方法的提供者,后面的程序都會(huì)調(diào)用它。內(nèi)部很簡(jiǎn)單,構(gòu)造函數(shù)接收一個(gè)字符串作為?name?,Run?方法輸出?"My?name?is?"?+?name?,而異步方法直接用委托的?BeginInvoke?和?EndInvoke?方法實(shí)現(xiàn)
public?class?AsyncDemo
{
//?Use?in?asynchronous?methods
private?delegate?string?runDelegate();
private?string?m_Name;
private?runDelegate?m_Delegate;
public?AsyncDemo(string?name)
{
m_Name?=?name;
m_Delegate?=?new?runDelegate(Run);
}
/**
///?Synchronous?method
///
///
public?string?Run()
{
return?"My?name?is?"?+?m_Name;
}
/**
///?Asynchronous?begin?method
///
///
///
///
public?IAsyncResult?BeginRun(AsyncCallback?callBack,?Object?stateObject)
{
try
{
return?m_Delegate.BeginInvoke(callBack,?stateObject);
}
catch(Exception?e)
{
//?Hide?inside?method?invoking?stack
throw?e;
}
}
/**
///?Asynchronous?end?method
///
///
///
public?string?EndRun(IAsyncResult?ar)
{
if?(ar?==?null)
throw?new?NullReferenceException("Arggument?ar?can't?be?null");
try
{
return?m_Delegate.EndInvoke(ar);
}
catch?(Exception?e)
{
//?Hide?inside?method?invoking?stack
throw?e;
}
}
????????????}?
?
首先是?Begin?之后直接調(diào)用?End?方法,當(dāng)然中間也可以做其他的操作
class?AsyncTest
{
static?void?Main(string[]?args)
{
AsyncDemo?demo?=?new?AsyncDemo("jiangnii");
//?Execute?begin?method
IAsyncResult?ar?=?demo.BeginRun(null,?null);
//?You?can?do?other?things?here
//?Use?end?method?to?block?thread?until?the?operation?is?complete
string?demoName?=?demo.EndRun(ar);
Console.WriteLine(demoName);
}
????????????}?
?
也可以用?IAsyncResult?的?AsyncWaitHandle?屬性,我在這里設(shè)置為1秒超時(shí)
class?AsyncTest
{
static?void?Main(string[]?args)
{
AsyncDemo?demo?=?new?AsyncDemo("jiangnii");
//?Execute?begin?method
IAsyncResult?ar?=?demo.BeginRun(null,?null);
//?You?can?do?other?things?here
//?Use?AsyncWaitHandle.WaitOne?method?to?block?thread?for?1?second?at?most
ar.AsyncWaitHandle.WaitOne(1000,?false);
if?(ar.IsCompleted)
{
//?Still?need?use?end?method?to?get?result,?
//?but?this?time?it?will?return?immediately
string?demoName?=?demo.EndRun(ar);
Console.WriteLine(demoName);
}
else
{
Console.WriteLine("Sorry,?can't?get?demoName,?the?time?is?over");
}
}
????????????}?
?
不中斷的輪循,每次循環(huán)輸出一個(gè)?"."
class?AsyncTest
{
static?void?Main(string[]?args)
{
AsyncDemo?demo?=?new?AsyncDemo("jiangnii");
//?Execute?begin?method
IAsyncResult?ar?=?demo.BeginRun(null,?null);
Console.Write("Waiting..");
while?(!ar.IsCompleted)
{
Console.Write(".");
//?You?can?do?other?things?here
}
Console.WriteLine();
//?Still?need?use?end?method?to?get?result,?
//?but?this?time?it?will?return?immediately
string?demoName?=?demo.EndRun(ar);
Console.WriteLine(demoName);
}
????????????}?
?
最后是使用回調(diào)方法并加上狀態(tài)對(duì)象,狀態(tài)對(duì)象被作為?IAsyncResult?參數(shù)的?AsyncState?屬性被傳給回調(diào)方法。回調(diào)方法執(zhí)行前不能讓主線程退出,我這里只是簡(jiǎn)單的讓其休眠了1秒。另一個(gè)與之前不同的地方是?AsyncDemo?對(duì)象被定義成了類的靜態(tài)字段,以便回調(diào)方法使用
?class?AsyncTest
{
static?AsyncDemo?demo?=?new?AsyncDemo("jiangnii");
static?void?Main(string[]?args)
{
//?State?object
bool?state?=?false;
//?Execute?begin?method
IAsyncResult?ar?=?demo.BeginRun(new?AsyncCallback(outPut),?state);
//?You?can?do?other?thins?here
//?Wait?until?callback?finished
System.Threading.Thread.Sleep(1000);
}
//?Callback?method
static?void?outPut(IAsyncResult?ar)
{
bool?state?=?(bool)ar.AsyncState;
string?demoName?=?demo.EndRun(ar);
if?(state)
{
Console.WriteLine(demoName);
}
else
{
Console.WriteLine(demoName?+?",?isn't?it?");
}
}
????????????}?
?
其他
對(duì)于一個(gè)已經(jīng)實(shí)現(xiàn)了?BeginOperationName?和?EndOperationName?方法的對(duì)象,我們可以直接用上述方式調(diào)用,但對(duì)于只有同步方法的對(duì)象,我們要對(duì)其進(jìn)行異步調(diào)用也不需要增加對(duì)應(yīng)的異步方法,而只需定義一個(gè)委托并使用其?BeginInvoke?和?EndInvoke?方法就可以了
轉(zhuǎn)載于:https://www.cnblogs.com/ruyi/archive/2009/07/14/1523510.html
總結(jié)
以上是生活随笔為你收集整理的[转] C#异步操作的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2019免费微信营销软件排行榜
- 下一篇: c# char unsigned_dll