非同步程式設計模式BeginInvoke和EndInvoke方法
轉載自:非同步程式設計模式BeginInvoke與EndInvoke方法
為什麼要進行非同步回撥?眾所周知,普通方法執行,是單執行緒的,如果中途有大型操作(如:讀取大檔案,大批量運算元據庫,網路傳輸等),都會導致方法阻塞,表現在介面上就是,程式卡或者死掉,介面元素不動了,不響應了。非同步方法很好的解決了這些問題,非同步執行某個方法,程式立即開闢一個新執行緒去執行你的方法,主執行緒包括介面就不會死掉了。非同步呼叫並不是要減少執行緒的開銷, 它的主要目的是讓呼叫方法的主執行緒不需要同步等待在這個函式呼叫上, 從而可以讓主執行緒繼續執行它下面的程式碼.
BeginInvoke方法可以使用執行緒非同步地執行委託所指向的方法。然後通過EndInvoke方法獲得方法的返回值(EndInvoke方法的返回值就是被呼叫方法的返回值),或是確定方法已經被成功呼叫。當使用BeginInvoke非同步呼叫方法時,如果方法未執行完,EndInvoke方法就會一直阻塞,直到被呼叫的方法執行完畢。
非同步呼叫通用模板
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- namespace AsyncCalculateFolderSize1
- {
- class Program
- {
- //計算指定資料夾的總容量
- private static long CalculateFolderSize(string FolderName)
- {
- if (Directory.Exists(FolderName) == false)
- {
- throw new DirectoryNotFoundException("資料夾不存在");
- }
- DirectoryInfo RootDir = new DirectoryInfo(FolderName);
- //獲取所有的子資料夾
- DirectoryInfo[] ChildDirs = RootDir.GetDirectories();
- //獲取當前資料夾中的所有檔案
- FileInfo[] files = RootDir.GetFiles();
- long totalSize = 0;
- //累加每個檔案的大小
- foreach (FileInfo file in files)
- {
- totalSize += file.Length;
- }
- //對每個資料夾執行同樣的計算過程:累加其下每個檔案的大小
- //這是通過遞迴呼叫實現的
- foreach (DirectoryInfo dir in ChildDirs)
- {
- totalSize += CalculateFolderSize(dir.FullName);
- }
- //返回資料夾的總容量
- return totalSize;
- }
- //定義一個委託
- public delegate long CalculateFolderSizeDelegate(string FolderName);
- static void Main(string[] args)
- {
- //定義一個委託變數引用靜態方法CalculateFolderSize
- CalculateFolderSizeDelegate d = CalculateFolderSize;
- Console.WriteLine("請輸入資料夾名稱(例如:C:\\Windows):");
- string FolderName = Console.ReadLine();
- //通過委託非同步呼叫靜態方法CalculateFolderSize
- IAsyncResult ret=d.BeginInvoke(FolderName,null,null);
- Console.WriteLine("正在計算中,請耐心等待……");
- //阻塞,等到呼叫完成,取出結果
- long size = d.EndInvoke(ret);
- Console.WriteLine("\n計算完成。資料夾{0}的容量為:{1}位元組\n", FolderName, size);
- }
- }
- }
非同步呼叫的奧祕
public delegate long CalculateFolderSizeDelegate(string FolderName);
- public sealed class CalculateFolderSizeDelegate: MulticastDelegate
- {
- public CalculateFolderSizeDelegate(Object target , intmethodPtr)
- { …… }
- public virtual long invoke(string FolderName)
- { …… }
- public virtual IAsyncResult BeginInvoke( string FolderName,
- AsyncCallbackcallback , object asyncState)
- { …… }
- public virtual long EndInvoke( IAsyncResultresult )
- { …… }
- }
(1)BeginInvoke方法用於啟動非同步呼叫
BeginInvoke()的函式宣告:
public IAsyncResult BeginInvoke(
<輸入和輸出變數>,回撥函式callback , 附加資訊AsyncState)
函式返回值型別:
public interface IAsyncResult
{
object AsyncState{ get;} //如果有回撥函式的話該引數用於儲存要傳遞給回撥函式的引數值
WaitHandle AsyncWaitHandle{ get;}
bool CompletedSynchronously{ get;}
bool IsCompleted{ get;} //儲存方法是否執行結束,我們可以通過該屬性的值來判斷非同步方法是否執行結束
}
1.BeginInvoke返回IasyncResult,可用於監視呼叫進度。
2.結果物件IAsyncResult是從開始操作返回的,並且可用於獲取有關非同步開始操作是否已完成的狀態。
3.結果物件被傳遞到結束操作,該操作返回撥用的最終返回值。
4.在開始操作中可以提供可選的回撥。如果提供回撥,在呼叫結束後,將呼叫該回撥;並且回撥中的程式碼可以呼叫結束操作。
5.如果需要將一些額外的資訊傳送給回撥函式,就將其放入BeginInvoke()方法的第3個引數asyncState中。注意到這個引數的型別為Object,所以可以放置任意型別的資料。如果有多個資訊需要傳送給回撥函式,可以將所有要傳送的資訊封狀到一個Struct變數,或者乾脆再定義一個類,將資訊封裝到這個類所建立的物件中,再傳送給BeginInvoke()方法。(2)EndInvoke方法用於檢索非同步呼叫結果。
方法宣告:
public <方法返回值型別>EndInvoke(<宣告為ref或out的引數>, IAsyncResult result )
1.result引數由BeginInvoke()方法傳回。.NET藉此以瞭解方法呼叫是否完成。
2.當EndInvoke方法發現非同步呼叫完成時,它取出此非同步呼叫方法的返回值作為其返回值,如果非同步呼叫方法有宣告為ref和out的引數,它也負責填充它。
3.在呼叫BeginInvoke後可隨時呼叫EndInvoke方法,注意:始終在非同步呼叫完成後呼叫EndInvoke.4.如果非同步呼叫未完成,EndInvoke將一直阻塞到非同步呼叫完成。
5.EndInvoke的引數包括需要非同步執行的方法的out和ref引數以及由BeginInvoke返回的IAsyncResult。
應用例項:
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- namespace AsyncCalculateFolderSize2
- {
- class Program
- {
- //計算指定資料夾的總容量
- private static long CalculateFolderSize(string FolderName)
- {
- if (Directory.Exists(FolderName) == false)
- {
- throw new DirectoryNotFoundException("資料夾不存在");
- }
- DirectoryInfo RootDir = new DirectoryInfo(FolderName);
- //獲取所有的子資料夾
- DirectoryInfo[] ChildDirs = RootDir.GetDirectories();
- //獲取當前資料夾中的所有檔案
- FileInfo[] files = RootDir.GetFiles();
- long totalSize = 0;
- //累加每個檔案的大小
- foreach (FileInfo file in files)
- {
- totalSize += file.Length;
- }
- //對每個資料夾執行同樣的計算過程:累加其下每個檔案的大小
- //這是通過遞迴呼叫實現的
- foreach (DirectoryInfo dir in ChildDirs)
- {
- totalSize += CalculateFolderSize(dir.FullName);
- }
- //返回資料夾的總容量
- return totalSize;
- }
- //定義一個委託
- public delegate long CalculateFolderSizeDelegate(string FolderName);
- static void Main(string[] args)
- {
- //定義一個委託變數引用靜態方法CalculateFolderSize
- CalculateFolderSizeDelegate d = CalculateFolderSize;
- Console.WriteLine("請輸入資料夾名稱(例如:C:\\Windows):");
- string FolderName = Console.ReadLine();
- //通過委託非同步呼叫靜態方法CalculateFolderSize
- IAsyncResult ret = d.BeginInvoke(FolderName, null, null);
- Console.Write ("正在計算中,請耐心等待");
- //每隔2秒檢查一次,輸出一個“."
- while (ret.IsCompleted == false)
- {
- Console.Write(".");
- System.Threading.Thread.Sleep(200);
- }
- //阻塞,等到呼叫完成,取出結果
- long size = d.EndInvoke(ret);
- Console.WriteLine("\n計算完成!\n資料夾{0}的容量為:{1}位元組", FolderName, size);
- }
- }
- }
2. 使用輪詢等待非同步呼叫完成:使用IAsyncResult的AsyncWaitHandle.WaitOne
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.IO;
- namespace AsyncCalculateFolderSize3
- {
- class Program
- {
- //計算指定資料夾的總容量
- private static long CalculateFolderSize(string FolderName)
- {
- if (Directory.Exists(FolderName) == false)
- {
- throw new DirectoryNotFoundException("資料夾不存在");
- }
- DirectoryInfo RootDir = new DirectoryInfo(FolderName);
- //獲取所有的子資料夾
- DirectoryInfo[] ChildDirs = RootDir.GetDirectories();
- //獲取當前資料夾中的所有檔案
- FileInfo[] files = RootDir.GetFiles();
- long totalSize = 0;
- //累加每個檔案的大小
- foreach (FileInfo file in files)
- {
- totalSize += file.Length;
- }
- //對每個資料夾執行同樣的計算過程:累加其下每個檔案的大小
- //這是通過遞迴呼叫實現的
- foreach (DirectoryInfo dir in ChildDirs)
- {
- totalSize += CalculateFolderSize(dir.FullName);
- }
- //返回資料夾的總容量
- return totalSize;
- }
- //定義一個委託
- public delegate long CalculateFolderSizeDelegate(string FolderName);
- static void Main(string[] args)
- {
- //定義一個委託變數引用靜態方法CalculateFolderSize
- CalculateFolderSizeDelegate d = CalculateFolderSize;
- Console.WriteLine("請輸入資料夾名稱(例如:C:\\Windows):");
- string FolderName = Console.ReadLine();
- //通過委託非同步呼叫靜態方法CalculateFolderSize
- IAsyncResult ret = d.BeginInvoke(FolderName, null, null);
- Console.Write("正在計算中,請耐心等待");
- while(!ret.AsyncWaitHandle.WaitOne(2000))
- {
- //等待2秒鐘,輸出一個“.”
- Console.Write(".");
- }
- //阻塞,等到呼叫完成,取出結果
- long size = d.EndInvoke(ret);
- Console.WriteLine("\n計算完成。資料夾{0}的容量為:{1}位元組\n", FolderName, size);
- }
- }
- }
3.使用非同步回撥函式
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- namespace AsyncCalculateFolderSize4
- {
- class Program
- {
- //計算指定資料夾的總容量
- private static long CalculateFolderSize(string FolderName)
- {
- if (Directory.Exists(FolderName) == false)
- {
- throw new DirectoryNotFoundException("資料夾不存在");
- }
- DirectoryInfo RootDir = new DirectoryInfo(FolderName);
- //獲取所有的子資料夾
- DirectoryInfo[] ChildDirs = RootDir.GetDirectories();
- //獲取當前資料夾中的所有檔案
- FileInfo[] files = RootDir.GetFiles();
- long totalSize = 0;
- //累加每個檔案的大小
- foreach (FileInfo file in files)
- {
- totalSize += file.Length;
- }
- //對每個資料夾執行同樣的計算過程:累加其下每個檔案的大小
- //這是通過遞迴呼叫實現的
- foreach (DirectoryInfo dir in ChildDirs)
- {
- totalSize += CalculateFolderSize(dir.FullName);
- }
- //返回資料夾的總容量
- return totalSize;
- }
- public delegate long CalculateFolderSizeDelegate(string FolderName);
- private static CalculateFolderSizeDelegate task = CalculateFolderSize;
- //用於回撥的函式
- public static void ShowFolderSize(IAsyncResult result)
- {
- long size = task.EndInvoke(result);
- Console.WriteLine("\n資料夾{0}的容量為:{1}位元組\n", (String)result.AsyncState, size);
- }
- static void Main(string[] args)
- {
- string FolderName;
- while (true)
- {
- Console.WriteLine("請輸入資料夾名稱(例如:C:\\Windows),輸入quit結束程式");
- FolderName = Console.ReadLine();
- if (FolderName == "quit")
- break;
- task.BeginInvoke(FolderName, ShowFolderSize, FolderName);//第一個引數是非同步函式的引數,第二個引數是回撥函式,第三個引數是回撥函式的引數,回撥函式會在非同步函式執行結束之後被呼叫。
- }
- }
- }
- }
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- namespace AsyncCalculateFolderSize6
- {
- class Program
- {
- //計算指定資料夾的總容量
- private static long CalculateFolderSize(string FolderName)
- {
- if (Directory.Exists(FolderName) == false)
- {
- throw new DirectoryNotFoundException("資料夾不存在");
- }
- DirectoryInfo RootDir = new DirectoryInfo(FolderName);
- //獲取所有的子資料夾
- DirectoryInfo[] ChildDirs = RootDir.GetDirectories();
- //獲取當前資料夾中的所有檔案
- FileInfo[] files = RootDir.GetFiles();
- long totalSize = 0;
- //累加每個檔案的大小
- foreach (FileInfo file in files)
- {
- totalSize += file.Length;
- }
- //對每個資料夾執行同樣的計算過程:累加其下每個檔案的大小
- //這是通過遞迴呼叫實現的
- foreach (DirectoryInfo dir in ChildDirs)
- {
- totalSize += CalculateFolderSize(dir.FullName);
- }
- //返回資料夾的總容量
- return totalSize;
- }
- //定義一個委託
- public delegate long CalculateFolderSizeDelegate(string FolderName);
- private static CalculateFolderSizeDelegate d = new CalculateFolderSizeDelegate(CalculateFolderSize);
- //用於回撥的函式
- public static void ShowFolderSize(IAsyncResult result)
- {
- try
- {
- long size = d.EndInvoke(result);
- while (Console.CursorLeft != 0)//只有使用者不輸入,且游標位於第一列時,才輸出資訊。
- {
- //等待2秒
- System.Threading.Thread.Sleep(2000);
- }
- Console.WriteLine("\n資料夾{0}的容量為:{1}位元組\n", (String)result.AsyncState, size);
- }
- catch (DirectoryNotFoundException e)
- {
- Console.WriteLine("您輸入的資料夾不存在");
- }
- }
- static void Main(string[] args)
- {
- string FolderName;
- while (true)
- {
- Console.WriteLine("請輸入資料夾名稱(例如:C:\\Windows),輸入quit結束程式");
- FolderName = Console.ReadLine();
- if (FolderName == "quit")
- break;
- d.BeginInvoke(FolderName, ShowFolderSize, FolderName);
- }
- }
- }
- }
個人程式碼:
class Program
{
public delegate string MyTestDelegate(string str);
static void Main(string[] args)
{
string param = "123";
MyTestDelegate d = Method;
AsyncCallback callBkcak = MethodCallKback;
IAsyncResult result = d.BeginInvoke(param, callBkcak, param);
string returnValue = d.EndInvoke(result);
Console.WriteLine(returnValue);
Console.ReadKey();
}
private static string Method(string str)
{
Console.WriteLine("非同步操作開始了!" + "你傳入的引數是:" + str);
return "非同步操作反回了一段話給你!";
}
private static void MethodCallKback(IAsyncResult result)
{
Console.WriteLine("回撥函式傳入引數:" + (string)result.AsyncState);
Console.WriteLine("非同步操作結束了!");
}
}
以下是輸出內容:相關文章
- Socket程式設計中的同步、非同步、阻塞和非阻塞(轉)程式設計非同步
- 程式設計之 同步靜態方法和單例模式的選擇程式設計單例模式
- python 網路程式設計----非阻塞或非同步程式設計Python程式設計非同步
- [譯] 非同步程式設計:阻塞與非阻塞非同步程式設計
- 非同步程式設計:基於事件的非同步程式設計模式(EAP)非同步程式設計事件設計模式
- JavaScript非同步程式設計助手:Promise模式JavaScript非同步程式設計Promise模式
- JavaScript非同步程式設計的Promise模式JavaScript非同步程式設計Promise模式
- 《JAVA併發程式設計實戰》原子變數和非阻塞同步機制Java程式設計變數
- Java網路程式設計和NIO詳解5:Java 非阻塞 IO 和非同步 IOJava程式設計非同步
- 阻塞式程式設計和非阻塞式程式設計區別程式設計
- 【設計模式】非同步阻塞、非同步回撥模式設計模式非同步
- Dart 非同步程式設計和原理Dart非同步程式設計
- 簡單理解非同步程式設計(python)和非同步程式設計(nodejs)非同步程式設計PythonNodeJS
- JavaScript非同步程式設計:非同步的資料收集方法JavaScript非同步程式設計
- Java設計模式之模板方法模式和建造者模式Java設計模式
- 【進階之路】併發程式設計(三)-非阻塞同步機制程式設計
- Java程式設計之設計模式之工廠方法模式全解Java程式設計設計模式
- JavaScript非同步程式設計的6種方法JavaScript非同步程式設計
- Javascript非同步程式設計的4種方法JavaScript非同步程式設計
- 同步、非同步、阻塞和非阻塞非同步
- Golang非同步程式設計方式和技巧Golang非同步程式設計
- Python程式設計風格和設計模式Python程式設計設計模式
- 設計模式——模版方法模式設計模式
- 設計模式-模板方法模式設計模式
- 設計模式 ——— 模板方法模式設計模式
- 一文徹底搞定(阻塞/非阻塞/同步/非同步)網路IO、併發程式設計模型、非同步程式設計模型的愛恨情仇非同步程式設計模型
- 非同步和非阻塞非同步
- 程式設計師的“非程式設計師”之路程式設計師
- Java設計模式(工廠方法設計模式)Java設計模式
- javascript非同步程式設計幾種方法簡介JavaScript非同步程式設計
- 設計模式-工廠方法模式的概述和使用-介面設計模式
- 程式設計師請注意:非同步程式設計模式已被人註冊為專利程式設計師非同步設計模式
- 非同步程式設計非同步程式設計
- Flutter非同步程式設計-async和awaitFlutter非同步程式設計AI
- 非科班程式設計師和科班程式設計師的差距到底在哪裡?程式設計師
- Java 網路程式設計 —— 非阻塞式程式設計Java程式設計
- Java設計模式-模板方法模式Java設計模式
- 設計模式之【模板方法模式】設計模式