C#編程總結(jié)(六)詳解異步編程
1、什么是異步?
異步操作通常用于執(zhí)行完成時(shí)間可能較長(zhǎng)的任務(wù),如打開大文件、連接遠(yuǎn)程計(jì)算機(jī)或查詢數(shù)據(jù)庫(kù)。異步操作在主應(yīng)用程序線程以外的線程中執(zhí)行。應(yīng)用程序調(diào)用方法異步執(zhí)行某個(gè)操作時(shí),應(yīng)用程序可在異步方法執(zhí)行其任務(wù)時(shí)繼續(xù)執(zhí)行。
2、同步與異步的區(qū)別
同步(Synchronous):在執(zhí)行某個(gè)操作時(shí),應(yīng)用程序必須等待該操作執(zhí)行完成后才能繼續(xù)執(zhí)行。
異步(Asynchronous):在執(zhí)行某個(gè)操作時(shí),應(yīng)用程序可在異步操作執(zhí)行時(shí)繼續(xù)執(zhí)行。實(shí)質(zhì):異步操作,啟動(dòng)了新的線程,主線程與方法線程并行執(zhí)行。
3、異步和多線程的區(qū)別
我們已經(jīng)知道,異步的實(shí)質(zhì)是開啟了新的線程。它與多線程的區(qū)別是什么呢?
簡(jiǎn)單的說(shuō)就是:異步線程是由線程池負(fù)責(zé)管理,而多線程,我們可以自己控制,當(dāng)然在多線程中我們也可以使用線程池。
就拿網(wǎng)絡(luò)扒蟲而言,如果使用異步模式去實(shí)現(xiàn),它使用線程池進(jìn)行管理。異步操作執(zhí)行時(shí),會(huì)將操作丟給線程池中的某個(gè)工作線程來(lái)完成。當(dāng)開始I/O操作的時(shí)候,異步會(huì)將工作線程還給線程池,這意味著獲取網(wǎng)頁(yè)的工作不會(huì)再占用任何CPU資源了。直到異步完成,即獲取網(wǎng)頁(yè)完畢,異步才會(huì)通過(guò)回調(diào)的方式通知線程池。可見,異步模式借助于線程池,極大地節(jié)約了CPU的資源。
注:DMA(Direct Memory Access)直接內(nèi)存存取,顧名思義DMA功能就是讓設(shè)備可以繞過(guò)處理器,直接由內(nèi)存來(lái)讀取資料。通過(guò)直接內(nèi)存訪問(wèn)的數(shù)據(jù)交換幾乎可以不損耗CPU的資源。在硬件中,硬盤、網(wǎng)卡、聲卡、顯卡等都有直接內(nèi)存訪問(wèn)功能。異步編程模型就是讓我們充分利用硬件的直接內(nèi)存訪問(wèn)功能來(lái)釋放CPU的壓力。
兩者的應(yīng)用場(chǎng)景:
計(jì)算密集型工作,采用多線程。
IO密集型工作,采用異步機(jī)制。
4、異步應(yīng)用
.NET Framework 的許多方面都支持異步編程功能,這些方面包括:
1)文件 IO、流 IO、套接字 IO。
2)網(wǎng)絡(luò)。
3)遠(yuǎn)程處理信道(HTTP、TCP)和代理。
4)使用 ASP.NET 創(chuàng)建的 XML Web services。
5)ASP.NET Web 窗體。
6)使用 MessageQueue 類的消息隊(duì)列。
.NET Framework 為異步操作提供兩種設(shè)計(jì)模式:
1)使用 IAsyncResult 對(duì)象的異步操作。
2)使用事件的異步操作。
IAsyncResult 設(shè)計(jì)模式允許多種編程模型,但更加復(fù)雜不易學(xué)習(xí),可提供大多數(shù)應(yīng)用程序都不要求的靈活性。可能的話,類庫(kù)設(shè)計(jì)者應(yīng)使用事件驅(qū)動(dòng)模型實(shí)現(xiàn)異步方法。在某些情況下,庫(kù)設(shè)計(jì)者還應(yīng)實(shí)現(xiàn)基于 IAsyncResult 的模型。
使用 IAsyncResult 設(shè)計(jì)模式的異步操作是通過(guò)名為 Begin操作名稱和End操作名稱的兩個(gè)方法來(lái)實(shí)現(xiàn)的,這兩個(gè)方法分別開始和結(jié)束異步操作操作名稱。例如,F(xiàn)ileStream 類提供 BeginRead 和 EndRead 方法來(lái)從文件異步讀取字節(jié)。這兩個(gè)方法實(shí)現(xiàn)了 Read 方法的異步版本。在調(diào)用 Begin操作名稱后,應(yīng)用程序可以繼續(xù)在調(diào)用線程上執(zhí)行指令,同時(shí)異步操作在另一個(gè)線程上執(zhí)行。每次調(diào)用 Begin操作名稱 時(shí),應(yīng)用程序還應(yīng)調(diào)用 End操作名稱來(lái)獲取操作的結(jié)果。Begin操作名稱 方法開始異步操作操作名稱并返回一個(gè)實(shí)現(xiàn) IAsyncResult 接口的對(duì)象。 .NET Framework 允許您異步調(diào)用任何方法。定義與您需要調(diào)用的方法具有相同簽名的委托;公共語(yǔ)言運(yùn)行庫(kù)將自動(dòng)為該委托定義具有適當(dāng)簽名的 BeginInvoke 和 EndInvoke 方法。
IAsyncResult 對(duì)象存儲(chǔ)有關(guān)異步操作的信息。下表提供了有關(guān)異步操作的信息。
名稱 |
說(shuō)明 |
AsyncState |
獲取用戶定義的對(duì)象,它限定或包含關(guān)于異步操作的信息。 |
AsyncWaitHandle |
獲取用于等待異步操作完成的 WaitHandle。 |
CompletedSynchronously |
獲取一個(gè)值,該值指示異步操作是否同步完成。 |
IsCompleted |
獲取一個(gè)值,該值指示異步操作是否已完 |
5、應(yīng)用實(shí)例
案例1-讀取文件
通常讀取文件是一個(gè)比較耗時(shí)的工作,特別是讀取大文件的時(shí)候,常見的上傳和下載。但是我們又不想讓用戶一直等待,用戶同樣可以進(jìn)行其他操作,可以使得系統(tǒng)有良好的交互性。這里我們寫了同步調(diào)用和異步調(diào)用來(lái)進(jìn)行比較說(shuō)明。
讀取文件類
using System; using System.IO; using System.Threading; namespace AsynSample { class FileReader { /// <summary> /// 緩存池 /// </summary> private byte[] Buffer { get; set; } /// <summary> /// 緩存區(qū)大小 /// </summary> public int BufferSize { get; set; } public FileReader(int bufferSize) { this.BufferSize = bufferSize; this.Buffer = new byte[BufferSize]; } /// <summary> /// 同步讀取文件 /// </summary> /// <param name="path">文件路徑</param> public void SynsReadFile(string path) { Console.WriteLine("同步讀取文件 begin"); using (FileStream fs = new FileStream(path, FileMode.Open)) { fs.Read(Buffer, 0, BufferSize); string output = System.Text.Encoding.UTF8.GetString(Buffer); Console.WriteLine("讀取的文件信息:{0}",output); } Console.WriteLine("同步讀取文件 end"); } /// <summary> /// 異步讀取文件 /// </summary> /// <param name="path"></param> public void AsynReadFile(string path) { Console.WriteLine("異步讀取文件 begin"); //執(zhí)行Endread時(shí)報(bào)錯(cuò),fs已經(jīng)釋放,注意在異步中不能使用釋放需要的資源 //using (FileStream fs = new FileStream(path, FileMode.Open)) //{ // Buffer = new byte[BufferSize]; // fs.BeginRead(Buffer, 0, BufferSize, AsyncReadCallback, fs); //} if (File.Exists(path)) { FileStream fs = new FileStream(path, FileMode.Open); fs.BeginRead(Buffer, 0, BufferSize, AsyncReadCallback, fs); } else { Console.WriteLine("該文件不存在"); } } /// <summary> /// /// </summary> /// <param name="ar"></param> void AsyncReadCallback(IAsyncResult ar) { FileStream stream = ar.AsyncState as FileStream; if (stream != null) { Thread.Sleep(1000); //讀取結(jié)束 stream.EndRead(ar); stream.Close(); string output = System.Text.Encoding.UTF8.GetString(this.Buffer); Console.WriteLine("讀取的文件信息:{0}", output); } } } }
測(cè)試用例
using System; using System.Threading; namespace AsynSample { class Program { static void Main(string[] args) { FileReader reader = new FileReader(1024); //改為自己的文件路徑 string path = "C:\\Windows\\DAI.log"; Console.WriteLine("開始讀取文件了..."); //reader.SynsReadFile(path); reader.AsynReadFile(path); Console.WriteLine("我這里還有一大灘事呢."); DoSomething(); Console.WriteLine("終于完事了,輸入任意鍵,歇著!"); Console.ReadKey(); } /// <summary> /// /// </summary> static void DoSomething() { Thread.Sleep(1000); for (int i = 0; i < 10000; i++) { if (i % 888 == 0) { Console.WriteLine("888的倍數(shù):{0}",i); } } } } }
輸出結(jié)果:
同步輸出:
異步輸出:
結(jié)果分析:
如果是同步讀取,在讀取時(shí),當(dāng)前線程讀取文件,只能等到讀取完畢,才能執(zhí)行以下的操作
而異步讀取,是創(chuàng)建了新的線程,讀取文件,而主線程,繼續(xù)執(zhí)行。我們可以開啟任務(wù)管理器來(lái)進(jìn)行監(jiān)視。
案例二--基于委托的異步操作
系統(tǒng)自帶一些類具有異步調(diào)用方式,如何使得自定義對(duì)象也具有異步功能呢?
我們可以借助委托來(lái)輕松實(shí)現(xiàn)異步。
說(shuō)到BeginInvoke,EndInvoke就不得不停下來(lái)看一下委托的本質(zhì)。為了便于理解委托,我定義一個(gè)簡(jiǎn)單的委托:
public delegate string MyFunc(int num, DateTime dt);
我們?cè)賮?lái)看一下這個(gè)委托在編譯后的程序集中是個(gè)什么樣的:
委托被編譯成一個(gè)新的類型,擁有BeginInvoke,EndInvoke,Invoke這三個(gè)方法。前二個(gè)方法的組合使用便可實(shí)現(xiàn)異步調(diào)用。第三個(gè)方法將以同步的方式調(diào)用。 其中BeginInvoke方法的最后二個(gè)參數(shù)用于回調(diào),其它參數(shù)則與委托的包裝方法的輸入?yún)?shù)是匹配的。 EndInvoke的返回值與委托的包裝方法的返回值匹配。
異步實(shí)現(xiàn)文件下載:
using System; using System.Text; namespace AsynSample { /// <summary> /// 下載委托 /// </summary> /// <param name="fileName"></param> public delegate string AysnDownloadDelegate(string fileName); /// <summary> /// 通過(guò)委托實(shí)現(xiàn)異步調(diào)用 /// </summary> class DownloadFile { /// <summary> /// 同步下載 /// </summary> /// <param name="fileName"></param> public string Downloading(string fileName) { string filestr = string.Empty; Console.WriteLine("下載事件開始執(zhí)行"); System.Threading.Thread.Sleep(3000); Random rand = new Random(); StringBuilder builder =new StringBuilder(); int num; for(int i=0;i<100;i++) { num = rand.Next(1000); builder.Append(i); } filestr = builder.ToString(); Console.WriteLine("下載事件執(zhí)行結(jié)束"); return filestr; } /// <summary> /// 異步下載 /// </summary> public IAsyncResult BeginDownloading(string fileName) { string fileStr = string.Empty; AysnDownloadDelegate downloadDelegate = new AysnDownloadDelegate(Downloading); return downloadDelegate.BeginInvoke(fileName, Downloaded, downloadDelegate); } /// <summary> /// 異步下載完成后事件 /// </summary> /// <param name="result"></param> private void Downloaded(IAsyncResult result) { AysnDownloadDelegate aysnDelegate = result.AsyncState as AysnDownloadDelegate; if (aysnDelegate != null) { string fileStr = aysnDelegate.EndInvoke(result); if (!string.IsNullOrEmpty(fileStr)) { Console.WriteLine("下載文件:{0}", fileStr); } else { Console.WriteLine("下載數(shù)據(jù)為空!"); } } else { Console.WriteLine("下載數(shù)據(jù)為空!"); } } } }
通過(guò)案例,我們發(fā)現(xiàn),使用委托能夠很輕易的實(shí)現(xiàn)異步。這樣,我們就可以自定義自己的異步操作了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:詳解C#的排列組合
欄 目:C#教程
下一篇:利用C#代碼實(shí)現(xiàn)圖片旋轉(zhuǎn)360度
本文標(biāo)題:C#編程總結(jié)(六)詳解異步編程
本文地址:http://mengdiqiu.com.cn/a1/C_jiaocheng/6154.html
您可能感興趣的文章
- 01-10C#編程實(shí)現(xiàn)自定義熱鍵的方法
- 01-10C#編程獲取資源文件中圖片的方法
- 01-10c# ArrayList的使用方法小總結(jié)
- 01-10C#實(shí)現(xiàn)子窗體與父窗體通信方法實(shí)例總結(jié)
- 01-10C#獲取客戶端相關(guān)信息實(shí)例總結(jié)
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類型和變量二
- 01-10C#編程自學(xué)之開篇介紹
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類型和變量三
- 01-10C#編程自學(xué)之運(yùn)算符和表達(dá)式
- 01-10C#編程自學(xué)之類和對(duì)象


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過(guò)反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁(yè)無(wú)法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過(guò)重寫Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-10delphi制作wav文件的方法
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 04-02jquery與jsp,用jquery
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?