天天看點

CLR線程池的I/O線程

I/O 線程是.NET專為通路外部資源所設定的一種線程,因為通路外部資源常常要受到外界因素的影響,為了防止讓主線程受影響而長期處于阻塞狀态,.NET為多個I/O操作都建立起了異步方法,例如:FileStream、TCP/IP、WebRequest、WebService等等,而且每個異步方法的使用方式都非常類似,都是以BeginXXX為開始,以EndXXX結束。

異步讀寫 FileStream

需要在 FileStream 異步調用 I/O線程,必須使用以下構造函數建立 FileStream 對象,并把useAsync設定為 true。

FileStream stream = new FileStream ( string path, FileMode mode, FileAccess access, FileShare share, int bufferSize,bool useAsync ) ;

 path ​是檔案的相對路徑或絕對路徑; mode 确定如何打開或建立檔案; access 确定​通路檔案的方式; share 确定檔案如何程序共享; bufferSize 是代表緩沖區大小,一般預設最小值為8,在啟動異步讀取或寫入時,檔案大小一般大于緩沖大小; userAsync代表是否啟動異步I/O線程。

異步寫入

FileStream中包含BeginWrite、EndWrite 方法可以啟動I/O線程進行異步寫入。

public override IAsyncResult BeginWrite ( byte[] array, int offset, int numBytes, AsyncCallback userCallback, Object stateObject )

public override void EndWrite (IAsyncResult asyncResult )

BeginWrite 傳回值為IAsyncResult, 使用方式與委托的BeginInvoke方法相似,最好就是使用回調函數,避免線程阻塞。在最後兩個參數中,參數AsyncCallback用于綁定回調函數; 參數Object用于傳遞外部資料。要注意一點:AsyncCallback所綁定的回調函數必須是帶單個 IAsyncResult 參數的無傳回值方法。

在例子中,把FileStream作為外部資料傳遞到回調函數當中,然後在回調函數中利用IAsyncResult.AsyncState擷取FileStream對象,最後通過FileStream.EndWrite(IAsyncResult)結束寫入。

1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             //把線程池的最大值設定為1000
 6             ThreadPool.SetMaxThreads(1000, 1000);
 7             ThreadPoolMessage("Start");
 8 
 9             //新立檔案File.sour
10             FileStream stream = new FileStream("File.sour", FileMode.OpenOrCreate, 
11                                        FileAccess.ReadWrite,FileShare.ReadWrite,1024,true);
12             byte[] bytes = new byte[16384];
13             string message = "An operating-system ThreadId has no fixed relationship........";
14             bytes = Encoding.Unicode.GetBytes(message);
15 
16             //啟動異步寫入
17             stream.BeginWrite(bytes, 0, (int)bytes.Length,new AsyncCallback(Callback),stream);
18             stream.Flush();
19             
20             Console.ReadKey();
21         }
22 
23         static void Callback(IAsyncResult result)
24         {
25             //顯示線程池現狀
26             Thread.Sleep(200);
27             ThreadPoolMessage("AsyncCallback");
28             //結束異步寫入
29             FileStream stream = (FileStream)result.AsyncState;
30             stream.EndWrite(result);
31             stream.Close();
32         }
33 
34         //顯示線程池現狀
35         static void ThreadPoolMessage(string data)
36         {
37             int a, b;
38             ThreadPool.GetAvailableThreads(out a, out b);
39             string message = string.Format("{0}\n  CurrentThreadId is {1}\n  "+
40                   "WorkerThreads is:{2}  CompletionPortThreads is :{3}",
41                   data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
42             Console.WriteLine(message);
43         }
44     }      

異步讀取

FileStream 中包含 BeginRead 與 EndRead 可以異步調用I/O線程進行讀取。

public override IAsyncResult BeginRead ( byte[] array,int offset,int numBytes, AsyncCallback userCallback,Object stateObject)

public override int EndRead(IAsyncResult asyncResult)

其使用方式與BeginWrite和EndWrite相似,AsyncCallback用于綁定回調函數; Object用于傳遞外部資料。在回調函數隻需要使用IAsyncResut.AsyncState就可擷取外部資料。EndWrite 方法會傳回從流讀取到的位元組數量。

首先定義 FileData 類,裡面包含FileStream對象,byte[] 數組和長度。然後把FileData對象作為外部資料傳到回調函數,在回調函數中,把IAsyncResult.AsyncState強制轉換為FileData,然後通過FileStream.EndRead(IAsyncResult)結束讀取。最後比較一下長度,若讀取到的長度與輸入的資料長度不一至,則抛出異常。

1      class Program
 2      {
 3          public class FileData
 4          {
 5              public FileStream Stream;
 6              public int Length;
 7              public byte[] ByteData;
 8          }
 9  
10          static void Main(string[] args)
11          {       
12              //把線程池的最大值設定為1000
13              ThreadPool.SetMaxThreads(1000, 1000);
14              ThreadPoolMessage("Start");
15              ReadFile();
16  
17              Console.ReadKey();
18          }
19  
20          static void ReadFile()
21          {
22              byte[] byteData=new byte[80961024];
23              FileStream stream = new FileStream("File1.sour", FileMode.OpenOrCreate, 
24                                      FileAccess.ReadWrite, FileShare.ReadWrite, 1024, true);
25              
26              //把FileStream對象,byte[]對象,長度等有關資料綁定到FileData對象中,以附帶屬性方式送到回調函數
27              FileData fileData = new FileData();
28              fileData.Stream = stream;
29              fileData.Length = (int)stream.Length;
30              fileData.ByteData = byteData;
31              
32              //啟動異步讀取
33              stream.BeginRead(byteData, 0, fileData.Length, new AsyncCallback(Completed), fileData);
34          }
35   
36          static void Completed(IAsyncResult result)
37          {
38              ThreadPoolMessage("Completed");
39  
40              //把AsyncResult.AsyncState轉換為FileData對象,以FileStream.EndRead完成異步讀取
41              FileData fileData = (FileData)result.AsyncState;
42              int length=fileData.Stream.EndRead(result);
43              fileData.Stream.Close();
44  
45              //如果讀取到的長度與輸入長度不一緻,則抛出異常
46              if (length != fileData.Length)
47                  throw new Exception("Stream is not complete!");
48  
49              string data=Encoding.ASCII.GetString(fileData.ByteData, 0, fileData.Length);
50              Console.WriteLine(data.Substring(2,22));
51          }
52  
53          //顯示線程池現狀
54          static void ThreadPoolMessage(string data)
55          {
56              int a, b;
57              ThreadPool.GetAvailableThreads(out a, out b);
58              string message = string.Format("{0}\n  CurrentThreadId is {1}\n  "+
59                           "WorkerThreads is:{2}  CompletionPortThreads is :{3}",
60                           data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
61              Console.WriteLine(message);      
62          }
63              
64    }