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 }