下面给出的代码是使用BeginXXX和EndXXX方法,让您可以异步执行代码的模式的一个例子:
using System;
using System.IO;
using System.Threading;
public sealed class Program {
public static void Main() {
byte[] buffer = new byte[100];
string filename = String.Concat(Environment.SystemDirectory, "\\ntdll.dll");
FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read,
FileShare.Read, 1024, FileOptions.Asynchronous);
IAsyncResult result = fs.BeginRead(buffer, 0, buffer.Length, null, null);
int numBytes = fs.EndRead(result);
fs.Close();
Console.WriteLine("Read {0} Bytes:", numBytes);
Console.WriteLine(BitConverter.ToString(buffer));
}
}
以下是结果输出:
Read 100 Bytes:
4D-5A-90-00-03-00-00-00-04-00-00-00-FF-FF-00-00-B8-00-00-00-00-00-00-00-40-00-00
-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-0
0-00-00-00-00-00-00-D0-00-00-00-0E-1F-BA-0E-00-B4-09-CD-21-B8-01-4C-CD-21-54-68-
69-73-20-70-72-6F-67-72-61-6D-20-63-61-6E-6E-6F-74-20-62-65
上面代码是使用异步APM模式来从一个文件流中读取100个字节。首先你得构建一个FileStream对象,为了完成异步操作,你必须在参数中指定FileOptions.Asynchronous。从文件流中同步读取数据,你会调用它的Read方法,该方法的原型如下:
public Int32 Read(Byte[] array, Int32 offset, Int32 count)
从这个同步读取数据的函数可以看出,这个函数会请求一个缓冲区,它会一直等到直到数据被读入缓冲区并返回。这并不是正确的做法,因为I/O的速度是不可预测的。在等待I/O完成之前,调用线程会一直挂起,它不做任何工作,这是在浪费资源。从文件异步读取数据,你会调用FileStream的BeginRead方法:
IAsyncResult BeginRead(Byte[] array, Int32 offset, Int32 numBytes,
AsyncCallback userCallback, object stateObject)
BeginRead方法和Read方法相似,不同的是BeginRead会返回一个IAsyncResult对象。在我们理解回调函数CallBack之后再来看这个对象。
等待-完成模式允许你异步调用和执行其它的任务,一旦这些任务完成,当其他的任务完成后,你可以尝试结束调用,线程会阻塞直到异步调用完成。下面的代码使用了这种模式:
using System;
using System.IO;
using System.Threading;
public sealed class Program {
public static void Main() {
byte[] buffer = new byte[100];
string filename = String.Concat(Environment.SystemDirectory, "\\ntdll.dll");
FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read,
FileShare.Read, 1024, FileOptions.Asynchronous);
// make the asynchronous call
fs.Read(buffer, 0, buffer.Length);
IAsyncResult result = fs.BeginRead(buffer, 0, buffer.Length, null, null);
// do some work here while you wait
//Calling EndRead will block until the Async work is complete
int numBytes = fs.EndRead(result);
// don't forget to close the stream
fs.Close();
Console.WriteLine("Read {0} Bytes:", numBytes);
Console.WriteLine(BitConverter.ToString(buffer));
}
}
以下是结果输出:
Read 100 Bytes:
20-72-75-6E-20-69-6E-20-44-4F-53-20-6D-6F-64-65-2E-0D-0D-0A-24-00-00-00-00-00-00
-00-34-CB-99-AF-70-AA-F7-FC-70-AA-F7-FC-70-AA-F7-FC-57-6C-8D-FC-71-AA-F7-FC-57-6
C-8A-FC-31-AA-F7-FC-57-6C-99-FC-5F-AA-F7-FC-57-6C-9A-FC-75-AB-F7-FC-57-6C-8B-FC-
71-AA-F7-FC-57-6C-8F-FC-71-AA-F7-FC-52-69-63-68-70-AA-F7-FC
在调用BeginXxx方法之后立即调用EndXxx方法是愚蠢的,因为调用线程会进入睡眠状态直到操作完成。不过如果你在BeginXXX和EndXXX之间放入一些东西的,事情就会大有玄机,我们可以看到在BeginXXX和EndXXX之间,代码会在读取数据的过程中执行别的任务,如下:
using System;
using System.IO;
using System.Threading;
public static class Program {
public static void Main() {
//ReadMultipleFiles(@"C:\Windows\system32\autoexec.NT", @"c:\Point.cs");
// Open the file indicating asynchronous I/O
FileStream fs = new FileStream(@"C:\windows\system32\autoexec.NT", FileMode.Open,
FileAccess.Read, FileShare.Read, 1024,
FileOptions.Asynchronous);
Byte[] data = new Byte[100];
// Initiate an asynchronous read operation against the FileStream
IAsyncResult ar = fs.BeginRead(data, 0, data.Length, null, null);
// Executing some other code here would be useful...
// Suspend this thread until the asynchronous
// operation completes and get the result
Int32 bytesRead = fs.EndRead(ar);
// No other operations to do, close the file
fs.Close();
// Now, it is OK to access the byte array and show the result.
Console.WriteLine("Number of bytes read={0}", bytesRead);
Console.WriteLine(BitConverter.ToString(data, 0, bytesRead));
}
private static void ReadMultipleFiles(params String[] pathnames) {
AsyncStreamRead[] asrs = new AsyncStreamRead[pathnames.Length];
for (Int32 n = 0; n < pathnames.Length; n++) {
// Open the file indicating asynchronous I/O
Stream stream = new FileStream(pathnames[n], FileMode.Open,
FileAccess.Read, FileShare.Read, 1024,
FileOptions.Asynchronous);
// Initiate an asynchronous read operation against the Stream
asrs[n] = new AsyncStreamRead(stream, 100);
}
// All streams have been opened and all read requests have been
// queued; they are all executing concurrently!
// Now, let's get and display the results
for (Int32 n = 0; n < asrs.Length; n++) {
Byte[] bytesRead = asrs[n].EndRead();
// Now, it is OK to access the byte array and show the result.
Console.WriteLine("Number of bytes read={0}", bytesRead.Length);
Console.WriteLine(BitConverter.ToString(bytesRead));
}
}
private sealed class AsyncStreamRead {
private Stream m_stream;
private IAsyncResult m_ar;
private Byte[] m_data;
public AsyncStreamRead(Stream stream, Int32 numBytes) {
m_stream = stream;
m_data = new Byte[numBytes];
// Initiate an asynchronous read operation against the Stream
m_ar = stream.BeginRead(m_data, 0, numBytes, null, null);
}
public Byte[] EndRead() {
// Suspend this thread until the asynchronous
// operation completes and get the result
Int32 numBytesRead = m_stream.EndRead(m_ar);
// No other operations to do, close the stream
m_stream.Close();
// Resize the array to save space
Array.Resize(ref m_data, numBytesRead);
// Return the bytes
return m_data;
}
}
}
以下是结果输出:
Number of bytes read=100
40-65-63-68-6F-20-6F-66-66-0D-0A-0D-0A-52-45-4D-20-41-55-54-4F-45-58-45-43-2E-42
-41-54-20-69-73-20-6E-6F-74-20-75-73-65-64-20-74-6F-20-69-6E-69-74-69-61-6C-69-7
A-65-20-74-68-65-20-4D-53-2D-44-4F-53-20-65-6E-76-69-72-6F-6E-6D-65-6E-74-2E-0D-
0A-52-45-4D-20-41-55-54-4F-45-58-45-43-2E-4E-54-20-69-73-20
所谓轮询模式,是我们通过轮询IAsyncResult来得知异步操作是否已经完成。这有两个例子:简单的和稍微复杂的。注意和记住的事情是,通过调用BeginRead返回的IAsyncResult对象的IsCompleted属性,我们可以继续根据需要做的工作,直到操作完成:
using System;
using System.IO;
using System.Threading;
public sealed class Program {
public static void Main() {
byte[] buffer = new byte[100];
string filename = String.Concat(Environment.SystemDirectory, "\\ntdll.dll");
FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read,
FileShare.Read, 1024, FileOptions.Asynchronous);
IAsyncResult result = fs.BeginRead(buffer, 0, buffer.Length, null, null);
while (!result.IsCompleted)
{
// do some work here if the call isn't completed
// you know, execute a code block or something
Thread.Sleep(100);
}
int numBytes = fs.EndRead(result);
fs.Close();
Console.WriteLine("Read {0} Bytes:", numBytes);
Console.WriteLine(BitConverter.ToString(buffer));
}
}
在回调模式中,我们需要指定一个方法进行回调。假设异步I / O请求,然后你的线程继续做任何它想做的事。当I / O请求完成后,窗口排队的工作项目中的CLR线程池。最后,线程池中的线程将出列的工作项目,并调用一些方法,你知道异步I/ O操作已完成。现在,在这个回调方法,你第一次调用EndXXX方法获得的异步操作的结果,然后继续处理结果的方法。当方法返回时,线程池中的线程可以追溯到入池可以服务其他排队的工作项目(或等待,直到一个显示了)。话虽如此,让我们回顾一下FileStream的BeginRead方法的原型:
IAsyncResult BeginRead(Byte[] array, Int32 offset,
Int32 numBytes, AsyncCallback userCallback, Object stateObject)
BeginRead方法需要两个参数,System.AsyncCallback和Object。AsyncCallback是一个委托类型定义如下:
delegate void AsyncCallback(IAsyncResult ar);
这有个例子,是引用Jeffrey Richter书中“The CLR via C#”:
// (code has been slightly revised)
using System;
using System.IO;
using System.Threading;
public static class Program {
// The array is static so it can be accessed by Main and ReadIsDone
private static Byte[] s_data = new Byte[100];
public static void Main() {
ReadMultipleFiles(@"C:\Windows\System32\config.NT", @"C:\point.cs");
APMCallbackUsingAnonymousMethod();
// Show the ID of the thread executing Main
Console.WriteLine("Main thread ID={0}",
Thread.CurrentThread.ManagedThreadId);
//ReadMultipleFiles(@"C:\Windows\System32\Config.NT", @"c:\Point.cs");
// Open the file indicating asynchronous I/O
FileStream fs = new FileStream(@"C:\Windows\System32\config.NT", FileMode.Open,
FileAccess.Read, FileShare.Read, 1024,
FileOptions.Asynchronous);
// Initiate an asynchronous read operation against the FileStream
// Pass the FileStream (fs) to the callback method (ReadIsDone)
fs.BeginRead(s_data, 0, s_data.Length, ReadIsDone, fs);
// Executing some other code here would be useful...
// For this demo, I'll just suspend the primary thread
Console.ReadLine();
}
private static void ReadIsDone(IAsyncResult ar) {
// Show the ID of the thread executing ReadIsDone
Console.WriteLine("ReadIsDone thread ID={0}",
Thread.CurrentThread.ManagedThreadId);
// Extract the FileStream (state) out of the IAsyncResult object
FileStream fs = (FileStream) ar.AsyncState;
// Get the result
Int32 bytesRead = fs.EndRead(ar);
// No other operations to do, close the file
fs.Close();
// Now, it is OK to access the byte array and show the result.
Console.WriteLine("Number of bytes read={0}", bytesRead);
Console.WriteLine(BitConverter.ToString(s_data, 0, bytesRead));
}
private static void APMCallbackUsingAnonymousMethod() {
// Show the ID of the thread executing Main
Console.WriteLine("Main thread ID={0}",
Thread.CurrentThread.ManagedThreadId);
// Open the file indicating asynchronous I/O
FileStream fs = new FileStream(@"C:\Windows\System32\config.NT", FileMode.Open,
FileAccess.Read, FileShare.Read, 1024,
FileOptions.Asynchronous);
Byte[] data = new Byte[100];
// Initiate an asynchronous read operation against the FileStream
// Pass the FileStream (fs) to the callback method (ReadIsDone)
fs.BeginRead(data, 0, data.Length,
delegate(IAsyncResult ar)
{
// Show the ID of the thread executing ReadIsDone
Console.WriteLine("ReadIsDone thread ID={0}",
Thread.CurrentThread.ManagedThreadId);
// Get the result
Int32 bytesRead = fs.EndRead(ar);
// No other operations to do, close the file
fs.Close();
// Now, it is OK to access the byte array and show the result.
Console.WriteLine("Number of bytes read={0}", bytesRead);
Console.WriteLine(BitConverter.ToString(data, 0, bytesRead));
}, null);
// Executing some other code here would be useful...
// For this demo, I'll just suspend the primary thread
Console.ReadLine();
}
private static void ReadMultipleFiles(params String[] pathnames) {
for (Int32 n = 0; n < pathnames.Length; n++) {
// Open the file indicating asynchronous I/O
Stream stream = new FileStream(pathnames[n], FileMode.Open,
FileAccess.Read, FileShare.Read, 1024,
FileOptions.Asynchronous);
// Initiate an asynchronous read operation against the Stream
new AsyncStreamRead(stream, 100,
delegate(Byte[] data)
{
// Process the data.
Console.WriteLine("Number of bytes read={0}", data.Length);
Console.WriteLine(BitConverter.ToString(data));
});
}
// All streams have been opened and all read requests have been
// queued; they are all executing concurrently and they will be
// processed as they complete!
// The primary thread could do other stuff here if it wants to...
// For this demo, I'll just suspend the primary thread
Console.ReadLine();
}
private delegate void StreamBytesRead(Byte[] streamData);
private sealed class AsyncStreamRead {
private Stream m_stream;
private Byte[] m_data;
StreamBytesRead m_callback;
public AsyncStreamRead(Stream stream, Int32 numBytes,
StreamBytesRead callback) {
m_stream = stream;
m_data = new Byte[numBytes];
m_callback = callback;
// Initiate an asynchronous read operation against the Stream
stream.BeginRead(m_data, 0, numBytes, ReadIsDone, null);
}
// Called when IO operation completes
private void ReadIsDone(IAsyncResult ar) {
Int32 numBytesRead = m_stream.EndRead(ar);
// No other operations to do, close the stream
m_stream.Close();
// Resize the array to save space
Array.Resize(ref m_data, numBytesRead);
// Call the application's callback method
m_callback(m_data);
}
}
}
以下是结果输出:
Number of bytes read=100
52-45-4D-20-57-69-6E-64-6F-77-73-20-4D-53-2D-44-4F-53-20-53-74-61-72-74-75-70-20
-46-69-6C-65-0D-0A-52-45-4D-0D-0A-52-45-4D-20-43-4F-4E-46-49-47-2E-53-59-53-20-7
6-73-20-43-4F-4E-46-49-47-2E-4E-54-0D-0A-52-45-4D-20-43-4F-4E-46-49-47-2E-53-59-
53-20-69-73-20-6E-6F-74-20-75-73-65-64-20-74-6F-20-69-6E-69
Number of bytes read=100
75-73-69-6E-67-20-53-79-73-74-65-6D-3B-0D-0A-0D-0A-70-75-62-6C-69-63-20-73-74-61
-74-69-63-20-63-6C-61-73-73-20-50-72-6F-67-72-61-6D-20-7B-0D-0A-20-20-20-70-75-6
2-6C-69-63-20-73-74-61-74-69-63-20-76-6F-69-64-20-4D-61-69-6E-28-73-74-72-69-6E-
67-5B-5D-20-61-72-67-73-29-20-7B-0D-0A-20-20-20-20-20-20-56
Main thread ID=1
ReadIsDone thread ID=4
Number of bytes read=100
52-45-4D-20-57-69-6E-64-6F-77-73-20-4D-53-2D-44-4F-53-20-53-74-61-72-74-75-70-20
-46-69-6C-65-0D-0A-52-45-4D-0D-0A-52-45-4D-20-43-4F-4E-46-49-47-2E-53-59-53-20-7
6-73-20-43-4F-4E-46-49-47-2E-4E-54-0D-0A-52-45-4D-20-43-4F-4E-46-49-47-2E-53-59-
53-20-69-73-20-6E-6F-74-20-75-73-65-64-20-74-6F-20-69-6E-69
下面的内容我们将理解如何使用线程池。在.NET中,我们需要从下面四个步骤开始:
1、创建一个不带任何参数且没有任何返回值的函数
2、创建一个ThreadStart委托,指定步骤1中的函数
3、创建一个Thread object,并指定步骤2中的ThreadStart委托
4、调用Thread.Start执行新的线程
下面是例子:
下面是例子:
using System;
using System.Threading;
public class App {
public static void Main() {
ThreadStart task = new ThreadStart(BasicWork);
Thread myThread = new Thread(task);
myThread.Start();
}
static void BasicWork()
{
Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId);
}
}
在现实世界中的线程,我们需要将信息传递到各个线程。请注意上面的例子中使用ThreadStart委托,它不带任何参数。OK。因此,我们需要将数据传递给线程。怎么办?通过使用一个新的委托叫做ParameterizedThreadStart。此委托指定的方法签名用一个Object类型的参数,并且没有返回。下面是一个代码示例将数据传递给一个线程:
using System;
using System.Threading;
public class App {
public static void Main() {
ParameterizedThreadStart task = new ParameterizedThreadStart(WorkWithParameter;
Thread myThread = new Thread(task);
myThread.Start("Whatcha doin?");
Thread newThread = new Thread(task);
newThread.Start("Nuthin much");
}
static void WorkWithParameter(object o)
{
string info = (string) o;
for (int x = 0; x < 10; ++x)
{
Console.WriteLine("{0}: {1}", info, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(10);
}
}
}
以下是结果输出:
Whatcha doin?: 3
Nuthin much: 4
Whatcha doin?: 3
Nuthin much: 4
Whatcha doin?: 3
Nuthin much: 4
Whatcha doin?: 3
Nuthin much: 4
Whatcha doin?: 3
Nuthin much: 4
Whatcha doin?: 3
Nuthin much: 4
Whatcha doin?: 3
Nuthin much: 4
. . . . . . .
在很多情况下,没有必要创建自己的线程,很多专业文档甚至不建议这样做。NET中的线程支持内置的线程池,在许多情况下你可能会认为你必须创建自己的线程。回想一下在前面的例子中使用的静态方法,将数据传递给一个线程:
static void WorkWithParameter(object o)
{
string info = (string) o;
for (int x = 0; x < 10; ++x)
{
Console.WriteLine("{0}: {1}", info, Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(10);
}
}
我们可以使用线程池的QueueUserWorkItem方法来替代上述代码完成的功能:
WaitCallback workItem = new WaitCallback(WorkWithParameter);
If (!ThreadPool.QueueUserWorkItem(workItem, “ThreadPooled”))
{
Console.WriteLine(“Could not queue item”);
}