C#線程學習筆記三:線程池中的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);
            }
        }
    }

    運行結果如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM