本筆記摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/20/MultiThreads.html,記錄一下學習過程以備後續查用。 一、I/O線程實現對文件的非同步 1.1 I/O線程介紹: 對於線程所執行的任務來說,可以把線程分為兩種類型:工作者線程和 ...
本筆記摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/20/MultiThreads.html,記錄一下學習過程以備後續查用。
一、I/O線程實現對文件的非同步
1.1 I/O線程介紹:
對於線程所執行的任務來說,可以把線程分為兩種類型:工作者線程和I/O線程。
工作者線程用來完成一些計算的任務,在任務執行的過程中,需要CPU不間斷地處理,所以,在工作者線程的執行過程中,CPU和線程的資源是充分利用的。
I/O線程主要用來完成輸入和輸出的工作,在這種情況下, 電腦需要I/O設備完成輸入和輸出的任務。在處理過程中,CPU是不需要參與處理過程的,此時正在運行的線程
將處於等待狀態,只有等任務完成後才會有事可做, 這樣就造成線程資源浪費的問題。為瞭解決這樣的問題,可以通過線程池來解決這樣的問題,讓線程池來管理線程。
對於I/O線程,我們可以將輸入輸出操作分成三個步驟:啟動、實際輸入輸出、處理結果。用於實際輸入輸出可由硬體完成,並不需要CPU的參與,而啟動和處理結果也可以
不在同一個線程上,這樣就可以充分利用線程資源。在.Net中通過以Begin開頭的方法來完成啟動,以End開頭的方法來處理結果,這兩個方法可以運行在不同的線程,這樣我們
就實現了非同步編程了。
1.2 .Net中如何使用非同步
註意:
其實當我們調用Begin開頭的方法,就是將一個I/O線程排入到線程池中(由.Net機制幫我們實現)。
註:工作者線程由線程池管理,直接調用ThreadPool.QueueUserWorkItem方法來將工作者線程排入到線程池中。
在.NET Framework中的FCL中有許多類型能夠對非同步操作提供支持,其中在FileStream類中就提供了對文件的非同步操作的方法。
FileStream類要調用I/O線程要實現非同步操作,首先要建立一個FileStream對象,然後通過下麵的構造函數來初始化FileStream對象實現非同步操作(非同步讀取和非同步寫入):
public FileStream (string path, FileMode mode, FileAccess access, FileShare share,int bufferSize,bool useAsync)
其中path代表文件的相對路徑或絕對路徑,mode代表如何打開或創建文件,access代表訪問文件的方式,share代表文件如何由進程共用,buffersize代表緩衝區的大小,
useAsync代表使用非同步I/O還是同步I/O,設置為true時,表示使用非同步I/O。
下麵代碼演示非同步寫入文件:
class Program { static void Main(string[] args) { #region I/O線程:非同步寫入文件 const int maxSize = 100000; ThreadPool.SetMaxThreads(1000, 1000); PrintMessage("Main thread start."); //初始化FileStream對象 FileStream fileStream = new FileStream("Test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 100, true); //列印文件流打開的方式 Console.WriteLine("Filestream is {0}opened with asynchronously.", fileStream.IsAsync ? "" : "not "); byte[] writeBytes = new byte[maxSize]; string writeMessage = "An operation use asynchronous method to write message......"; writeBytes = Encoding.Unicode.GetBytes(writeMessage); Console.WriteLine("Message sizes is:{0} bytes.\n", writeBytes.Length); //調用非同步寫入方法將信息寫入到文件中 fileStream.BeginWrite(writeBytes, 0, writeBytes.Length, new AsyncCallback(EndWriteCallback), fileStream); fileStream.Flush(); Console.Read(); #endregion } /// <summary> /// 列印線程池信息 /// </summary> /// <param name="data"></param> private static void PrintMessage(string data) { //獲得線程池中可用的工作者線程數量及I/O線程數量 ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber); Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n", data, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground.ToString(), workThreadNumber.ToString(), ioThreadNumber.ToString()); } /// <summary> /// 當數據寫入文件完成後調用此方法來結束非同步寫操作 /// </summary> /// <param name="asyncResult"></param> private static void EndWriteCallback(IAsyncResult asyncResult) { Thread.Sleep(500); PrintMessage("Asynchronous method start."); FileStream filestream = asyncResult.AsyncState as FileStream; //結束非同步寫入數據 filestream.EndWrite(asyncResult); filestream.Close(); } }
運行結果如下:
從運行結果可以看出,此時是調用線程池中的I/O線程去執行回調函數的,同時在項目的bin\Debug文件目錄下生成了一個Test.txt文件。
下麵代碼演示非同步讀取文件:
class Program { //非同步讀取文件 const int maxSize = 1024; private static readonly byte[] readBytes = new byte[maxSize]; static void Main(string[] args) { #region I/O線程:非同步讀取文件 ThreadPool.SetMaxThreads(1000, 1000); PrintMessage("Main thread start."); // 初始化FileStream對象 FileStream fileStream = new FileStream("Test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 100, false); // 非同步讀取文件內容 fileStream.BeginRead(readBytes, 0, readBytes.Length, new AsyncCallback(EndReadCallback), fileStream); Console.Read(); #endregion } /// <summary> /// 列印線程池信息 /// </summary> /// <param name="data"></param> private static void PrintMessage(string data) { //獲得線程池中可用的工作者線程數量及I/O線程數量 ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber); Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n", data, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground.ToString(), workThreadNumber.ToString(), ioThreadNumber.ToString()); } /// <summary> /// 當數據讀取文件完成後調用此方法來結束非同步寫操作 /// </summary> /// <param name="asyncResult"></param> private static void EndReadCallback(IAsyncResult asyncResult) { Thread.Sleep(1000); PrintMessage("Asynchronous method start."); // 把AsyncResult.AsyncState轉換為State對象 FileStream readStream = (FileStream)asyncResult.AsyncState; int readLength = readStream.EndRead(asyncResult); if (readLength <= 0) { Console.WriteLine("Read error."); return; } string readMessage = Encoding.Unicode.GetString(readBytes, 0, readLength); Console.WriteLine("Read message is :" + readMessage); readStream.Close(); } }
運行結果如下:
二、I/O線程實現對請求的非同步
我們同樣可以利用I/O線程來模擬瀏覽器對伺服器請求的非同步操作,在.NET類庫中的WebRequest類提供了非同步請求的支持。
下麵代碼演示非同步請求:
class Program { static void Main(string[] args) { #region I/O線程:非同步請求 ThreadPool.SetMaxThreads(1000, 1000); PrintMessage("Main thread start."); // 發出一個非同步Web請求 WebRequest webrequest = WebRequest.Create("https://www.cnblogs.com/"); webrequest.BeginGetResponse(ProcessWebResponse, webrequest); Console.Read(); #endregion } /// <summary> /// 列印線程池信息 /// </summary> /// <param name="data"></param> private static void PrintMessage(string data) { //獲得線程池中可用的工作者線程數量及I/O線程數量 ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber); Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n", data, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground.ToString(), workThreadNumber.ToString(), ioThreadNumber.ToString()); } /// <summary> /// Web請求回調函數 /// </summary> /// <param name="result"></param> private static void ProcessWebResponse(IAsyncResult result) { Thread.Sleep(500); PrintMessage("Asynchronous method start."); WebRequest webRequest = (WebRequest)result.AsyncState; using (WebResponse wr = webRequest.EndGetResponse(result)) { Console.WriteLine("Content length is : " + wr.ContentLength); } } }
運行結果如下: