| 引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发。C#综合揭秘——细说多线程(上) 目录 一、线程的定义 二、线程的基础知识 三、以ThreadStart方式实现多线程 四、CLR线程池的工作者线程 五、CLR线程池的I/O线程 六、异步 SqlCommand 七、并行编程与PLINQ 八、计时器与锁  五、CLR线程池的I/O线程 在前一节所介绍的线程都属于CLR线程池的工作者线程,这一节开始为大家介绍一下CLR线程池的I/O线程 I/O 线程是.NET专为访问外部资源所设置的一种线程,因为访问外部资源常常要受到外界因素的影响,为了防止让主线程受影响而长期处于阻塞状态,.NET为多个I/O操作都建立起了异步方法,例如:FileStream、TCP/IP、WebRequest、WebService等等,而且每个异步方法的使用方式都非常类似,都是以BeginXXX为开始,以EndXXX结束,下面为大家一一解说。 5.1 异步读写 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线程。 注意:当使用 BeginRead 和 BeginWrite 方法在执行大量读或写时效果更好,但对于少量的读/写,这些方法速度可能比同步读取还要慢,因为进行线程间的切换需要大量时间。 5.1.1 异步写入 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)结束写入。 
                           
                            | class Program
    {
        static void Main(string[] args)
        {
            //把线程池的最大值设置为1000
            ThreadPool.SetMaxThreads(1000, 1000);
            ThreadPoolMessage("Start");
            //新立文件File.sour
            FileStream stream = new FileStream("File.sour", FileMode.OpenOrCreate, 
                                       FileAccess.ReadWrite,FileShare.ReadWrite,1024,true);
            byte[] bytes = new byte[16384];
            string message = "An operating-system ThreadId has no fixed relationship........";
            bytes = Encoding.Unicode.GetBytes(message);
            //启动异步写入
            stream.BeginWrite(bytes, 0, (int)bytes.Length,new AsyncCallback(Callback),stream);
            stream.Flush();
            
            Console.ReadKey();
        }
        static void Callback(IAsyncResult result)
        {
            //显示线程池现状
            Thread.Sleep(200);
            ThreadPoolMessage("AsyncCallback");
            //结束异步写入
            FileStream stream = (FileStream)result.AsyncState;
            stream.EndWrite(result);
            stream.Close();
        }
        //显示线程池现状
        static void ThreadPoolMessage(string data)
        {
            int a, b;
            ThreadPool.GetAvailableThreads(out a, out b);
            string message = string.Format("{0}\n  CurrentThreadId is {1}\n  "+
                  "WorkerThreads is:{2}  CompletionPortThreads is :{3}",
                  data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
            Console.WriteLine(message);
        }
    }
 |  由输出结果可以看到,在使用FileStream.BeginWrite方法后,系统将自动启动CLR线程池中I/O线程。 
 5.1.2 异步读取 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)结束读取。最后比较一下长度,若读取到的长度与输入的数据长度不一至,则抛出异常。 
                           
                            | class Program
     {
         public class FileData
         {
             public FileStream Stream;
             public int Length;
             public byte[] ByteData;
         }
 
         static void Main(string[] args)
         {       
             //把线程池的最大值设置为1000
             ThreadPool.SetMaxThreads(1000, 1000);
             ThreadPoolMessage("Start");
             ReadFile();
 
             Console.ReadKey();
         }
 
         static void ReadFile()
         {
             byte[] byteData=new byte[80961024];
             FileStream stream = new FileStream("File1.sour", FileMode.OpenOrCreate, 
                                     FileAccess.ReadWrite, FileShare.ReadWrite, 1024, true);
             
             //把FileStream对象,byte[]对象,长度等有关数据绑定到FileData对象中,以附带属性方式送到回调函数
             FileData fileData = new FileData();
             fileData.Stream = stream;
             fileData.Length = (int)stream.Length;
             fileData.ByteData = byteData;
             
             //启动异步读取
             stream.BeginRead(byteData, 0, fileData.Length, new AsyncCallback(Completed), fileData);
         }
  
         static void Completed(IAsyncResult result)
         {
             ThreadPoolMessage("Completed");
 
             //把AsyncResult.AsyncState转换为FileData对象,以FileStream.EndRead完成异步读取
             FileData fileData = (FileData)result.AsyncState;
             int length=fileData.Stream.EndRead(result);
             fileData.Stream.Close();
 
             //如果读取到的长度与输入长度不一致,则抛出异常
             if (length != fileData.Length)
                 throw new Exception("Stream is not complete!");
 
             string data=Encoding.ASCII.GetString(fileData.ByteData, 0, fileData.Length);
             Console.WriteLine(data.Substring(2,22));
         }
 
         //显示线程池现状
         static void ThreadPoolMessage(string data)
         {
             int a, b;
             ThreadPool.GetAvailableThreads(out a, out b);
             string message = string.Format("{0}\n  CurrentThreadId is {1}\n  "+
                          "WorkerThreads is:{2}  CompletionPortThreads is :{3}",
                          data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
             Console.WriteLine(message);      
         }
             
   }
 |  由输出结果可以看到,在使用FileStream.BeginRead方法后,系统将自动启动CLR线程池中I/O线程。 
 注意:如果你看到的测试结果正好相反:工作者线程为999,I/O线程为1000,这是因为FileStream的文件容量小于缓冲值1024所致的。此时文件将会一次性读取或写入,而系统将启动工作者线程而非I/O线程来处理回调函数。 5.2 异步操作TCP/IP套接字 在介绍 TCP/IP 套接字前先简单介绍一下 NetworkStream 
                          类,它是用于网络访问的基础数据流。 NetworkStream 提供了好几个方法控制套接字数据的发送与接收, 
                          其中BeginRead、EndRead、BeginWrite、EndWrite 能够实现异步操作,而且异步线程是来自于CLR线程池的I/O线程。 
                           
                            | public override int ReadByte ()
public override int Read (byte[] buffer,int offset, int size)
public override void WriteByte (byte value)
public override void Write (byte[] buffer,int offset, int size)
public override IAsyncResult BeginRead (byte [] buffer, int offset, int size,  AsyncCallback callback, Object state )
public override int EndRead(IAsyncResult result)
public override IAsyncResult BeginWrite (byte [] buffer, int offset, int size,  AsyncCallback callback, Object state )
public override void EndWrite(IAsyncResult result)
 |  若要创建 NetworkStream,必须提供已连接的 Socket。而在.NET中使用TCP/IP套接字不需要直接与Socket打交道,因为.NET把Socket的大部分操作都放在System.Net.TcpListener和System.Net.Sockets.TcpClient里面,这两个类大大地简化了Socket的操作。一般套接字对象Socket包含一个Accept()方法,此方法能产生阻塞来等待客户端的请求,而在TcpListener类里也包含了一个相似的方法 
                          public TcpClient AcceptTcpClient()用于等待客户端的请求。此方法将会返回一个TcpClient 
                          对象,通过 TcpClient 的 public NetworkStream GetStream()方法就能获取NetworkStream对象,控制套接字数据的发送与接收。 下面以一个例子说明异步调用TCP/IP套接字收发数据的过程。 首先在服务器端建立默认地址127.0.0.1用于收发信息,使用此地址与端口500新建TcpListener对象,调用TcpListener.Start 
                          侦听传入的连接请求,再使用一个死循环来监听信息。 在ChatClient类包括有接收信息与发送信息两个功能:当接收到客户端请求时,它会利用 
                          NetworkStream.BeginRead 读取客户端信息,并在回调函数ReceiveAsyncCallback中输出信息内容,若接收到的信息的大小小于1时,它将会抛出一个异常。当信息成功接收后,再使用 
                          NetworkStream.BeginWrite 方法回馈信息到客户端。 
                           
                            | class Program
    {
        static void Main(string[] args)
        {
            //设置CLR线程池最大线程数
            ThreadPool.SetMaxThreads(1000, 1000);
   
            //默认地址为127.0.0.1
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            TcpListener tcpListener = new TcpListener(ipAddress, 500);
            tcpListener.Start();
            
            //以一个死循环来实现监听
            while (true)
            {   //调用一个ChatClient对象来实现监听
                ChatClient chatClient = new ChatClient(tcpListener.AcceptTcpClient());    
            }
        }
    }
    public class ChatClient
    {
        static TcpClient tcpClient;
        static byte[] byteMessage;
        static string clientEndPoint;
        public ChatClient(TcpClient tcpClient1)
        {
            tcpClient = tcpClient1;
            byteMessage = new byte[tcpClient.ReceiveBufferSize];
           
            //显示客户端信息
            clientEndPoint = tcpClient.Client.RemoteEndPoint.ToString();
            Console.WriteLine("Client's endpoint is " + clientEndPoint);
            
            //使用NetworkStream.BeginRead异步读取信息
            NetworkStream networkStream = tcpClient.GetStream();
            networkStream.BeginRead(byteMessage, 0, tcpClient.ReceiveBufferSize,
                                         new AsyncCallback(ReceiveAsyncCallback), null);
        }
        public void ReceiveAsyncCallback(IAsyncResult iAsyncResult)
        {
            //显示CLR线程池状态
            Thread.Sleep(100);
            ThreadPoolMessage("\nMessage is receiving");
            //使用NetworkStream.EndRead结束异步读取
            NetworkStream networkStreamRead = tcpClient.GetStream();
            int length=networkStreamRead.EndRead(iAsyncResult);
            //如果接收到的数据长度少于1则抛出异常
            if (length < 1)
            {
                tcpClient.GetStream().Close();
                throw new Exception("Disconnection!");
            }
            //显示接收信息
            string message = Encoding.UTF8.GetString(byteMessage, 0, length);
            Console.WriteLine("Message:" + message);
            //使用NetworkStream.BeginWrite异步发送信息
            byte[] sendMessage = Encoding.UTF8.GetBytes("Message is received!");
            NetworkStream networkStreamWrite=tcpClient.GetStream();
            networkStreamWrite.BeginWrite(sendMessage, 0, sendMessage.Length, 
                                            new AsyncCallback(SendAsyncCallback), null);
        }
        //把信息转换成二进制数据,然后发送到客户端
        public void SendAsyncCallback(IAsyncResult iAsyncResult)
        {
            //显示CLR线程池状态
            Thread.Sleep(100);
            ThreadPoolMessage("\nMessage is sending");
            //使用NetworkStream.EndWrite结束异步发送
            tcpClient.GetStream().EndWrite(iAsyncResult);
            //重新监听
            tcpClient.GetStream().BeginRead(byteMessage, 0, tcpClient.ReceiveBufferSize,
                                               new AsyncCallback(ReceiveAsyncCallback), null);
        }
        //显示线程池现状
        static void ThreadPoolMessage(string data)
        {
            int a, b;
            ThreadPool.GetAvailableThreads(out a, out b);
            string message = string.Format("{0}\n  CurrentThreadId is {1}\n  " +
                  "WorkerThreads is:{2}  CompletionPortThreads is :{3}\n",
                  data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
            Console.WriteLine(message);
        }
    }
 |  而在客户端只是使用简单的开发方式,利用TcpClient连接到服务器端,然后调用NetworkStream.Write方法发送信息,最后调用NetworkStream.Read方法读取回馈信息 
                           
                            | static void Main(string[] args)
        {
            //连接服务端
            TcpClient tcpClient = new TcpClient("127.0.0.1", 500);
            //发送信息
            NetworkStream networkStream = tcpClient.GetStream();
            byte[] sendMessage = Encoding.UTF8.GetBytes("Client request connection!");
            networkStream.Write(sendMessage, 0, sendMessage.Length);
            networkStream.Flush();
            //接收信息
            byte[] receiveMessage=new byte[1024];
            int count=networkStream.Read(receiveMessage, 0,1024);
            Console.WriteLine(Encoding.UTF8.GetString(receiveMessage));
            Console.ReadKey();
        }
 |  注意观察运行结果,服务器端的异步操作线程都是来自于CLR线程池的I/O线程 
 5.3 异步WebRequest System.Net.WebRequest 是 .NET 为实现访问 Internet 的 “请求/响应模型” 
                          而开发的一个 abstract 基类, 它主要有三个子类:FtpWebRequest、HttpWebRequest、FileWebRequest。当使用WebRequest.Create(string 
                          uri)创建对象时,应用程序就可以根据请求协议判断实现类来进行操作。FileWebRequest、FtpWebRequest、HttpWebRequest 
                          各有其作用:FileWebRequest 使用 “file://路径” 的URI方式实现对本地资源和内部文件的请求/响应、FtpWebRequest 
                          使用FTP文件传输协议实现文件请求/响应、HttpWebRequest 用于处理HTTP的页面请求/响应。由于使用方法相类似,下面就以常用的HttpWebRequest为例子介绍一下异步WebRequest的使用方法。 在使用ASP.NET开发网站的时候,往往会忽略了HttpWebRequest的使用,因为开发都假设客户端是使用浏览器等工具去阅读页面的。但如果你对REST开发方式有所了解,那对 
                          HttpWebRequest 就应该非常熟悉。它可以在路径参数、头文件、页面主体、Cookie 等多处地方加入请求条件,然后对回复数据进行适当处理。HttpWebRequest 
                          包含有以下几个常用方法用于处理请求/响应: 
                           
                            | public override Stream GetRequestStream ()
public override WebResponse GetResponse ()
public override IAsyncResult BeginGetRequestStream ( AsyncCallback callback, Object state )
public override Stream EndGetRequestStream ( IAsyncResult asyncResult )
public override IAsyncResult BeginGetResponse ( AsyncCallback callback, Object state )
public override WebResponse EndGetResponse ( IAsyncResult asyncResult )
 |  其中BeginGetRequestStream、EndGetRequestStream 用于异步向HttpWebRequest对象写入请求信息; 
                          BeginGetResponse、EndGetResponse 用于异步发送页面请求并获取返回信息。使用异步方式操作Internet的“请求/响应”,避免主线程长期处于等待状态,而操作期间异步线程是来自CLR线程池的I/O线程。 注意:请求与响应不能使用同步与异步混合开发模式,即当请求写入使用GetRequestStream同步模式,即使响应使用BeginGetResponse异步方法,操作也与GetRequestStream方法在于同一线程内。 下面以简单的例子介绍一下异步请求的用法。 首先为Person类加上可序列化特性,在服务器端建立Hanlder.ashx,通过Request.InputStream 
                          获取到请求数据并把数据转化为String对象,此实例中数据是以 “Id:1” 的形式实现传送的。然后根据Id查找对应的Person对象,并把Person对象写入Response.OutStream 
                          中返还到客户端。 在客户端先把 HttpWebRequird.Method 设置为 "post",使用异步方式通过BeginGetRequireStream获取请求数据流,然后写入请求数据 
                          “Id:1”。再使用异步方法BeginGetResponse 获取回复数据,最后把数据反序列化为Person对象显示出来。 注意:HttpWebRequire.Method默认为get,在写入请求前必须把HttpWebRequire.Method设置为post,否则在使用BeginGetRequireStream 
                          获取请求数据流的时候,系统就会发出 “无法发送具有此谓词类型的内容正文" 的异常。 Model 
                           
                            | namespace Model
{
    [Serializable]
    public class Person
    {
        public int ID
        {
            get;
            set;
        }
        public string Name
        {
            get;
            set;
        }
        public int Age
        {
            get;
            set;
        }
    }
}
 |  服务器端 
                           
                            | public class Handler : IHttpHandler {
    public void ProcessRequest(HttpContext context)
    {
        //把信息转换为String,找出输入条件Id
        byte[] bytes=new byte[1024];
        int length=context.Request.InputStream.Read(bytes,0,1024);
        string condition = Encoding.Default.GetString(bytes);
        int id = int.Parse(condition.Split(new string[] { ":" }, 
                           StringSplitOptions.RemoveEmptyEntries)[1]);
        
        //根据Id查找对应Person对象
        var person = GetPersonList().Where(x => x.ID == id).First();
        
        //所Person格式化为二进制数据写入OutputStream
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(context.Response.OutputStream, person);
    }
    //模拟源数据
    private IList GetPersonList()
    {
        var personList = new List();
        
        var person1 = new Person();
        person1.ID = 1;
        person1.Name = "Leslie";
        person1.Age = 30;
        personList.Add(person1);
        ...........
        return personList;
    }
    public bool IsReusable
    {
        get { return true;}
    }
}
 |  客户端 
                           
                            | class Program
    {
        static void Main(string[] args)
        {
            ThreadPool.SetMaxThreads(1000, 1000);
            Request();
            Console.ReadKey();
        }
        static void Request()
        {
            ThreadPoolMessage("Start"); 
            //使用WebRequest.Create方法建立HttpWebRequest对象
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(
                                            "http://localhost:5700/Handler.ashx");
            webRequest.Method = "post";
           
            //对写入数据的RequestStream对象进行异步请求
            IAsyncResult result=webRequest.BeginGetRequestStream(
                new AsyncCallback(EndGetRequestStream),webRequest);
        }
        static void EndGetRequestStream(IAsyncResult result)
        {
            ThreadPoolMessage("RequestStream Complete");
            //获取RequestStream
            HttpWebRequest webRequest = (HttpWebRequest)result.AsyncState;
            Stream stream=webRequest.EndGetRequestStream(result);
            //写入请求条件
            byte[] condition = Encoding.Default.GetBytes("Id:1");
            stream.Write(condition, 0, condition.Length);
            //异步接收回传信息
            IAsyncResult responseResult = webRequest.BeginGetResponse(
                new AsyncCallback(EndGetResponse), webRequest);
        }
        static void EndGetResponse(IAsyncResult result)
        {
            //显出线程池现状
            ThreadPoolMessage("GetResponse Complete");
            //结束异步请求,获取结果
            HttpWebRequest webRequest = (HttpWebRequest)result.AsyncState;
            WebResponse webResponse = webRequest.EndGetResponse(result);
            
            //把输出结果转化为Person对象
            Stream stream = webResponse.GetResponseStream();
            BinaryFormatter formatter = new BinaryFormatter();
            var person=(Person)formatter.Deserialize(stream);
            Console.WriteLine(string.Format("Person    Id:{0} Name:{1} Age:{2}",
                person.ID, person.Name, person.Age));
        }
        //显示线程池现状
        static void ThreadPoolMessage(string data)
        {
            int a, b;
            ThreadPool.GetAvailableThreads(out a, out b);
            string message = string.Format("{0}\n  CurrentThreadId is {1}\n  " +
                  "WorkerThreads is:{2}  CompletionPortThreads is :{3}\n",
                  data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
            Console.WriteLine(message);
        }
    }
 |  从运行结果可以看到,BeginGetRequireStream、BeginGetResponse方法是使用CLR线程池的I/O线程。 
 5.4 异步调用WebService 相比TCP/IP套接字,在使用WebService的时候,服务器端需要更复杂的操作处理,使用时间往往会更长。为了避免客户端长期处于等待状态,在配置服务引用时选择 
                          “生成异步操作”,系统可以自动建立异步调用的方式。 以.NET 2.0以前,系统都是使用ASMX来设计WebService,而近年来WCF可说是火热登场,下面就以WCF为例子简单介绍一下异步调用WebService的例子。 由于系统可以自动生成异步方法,使用起来非常简单,首先在服务器端建立服务ExampleService,里面包含方法Method。客户端引用此服务时,选择 
                          “生成异步操作”。然后使用 BeginMethod 启动异步方法, 在回调函数中调用EndMethod结束异步调用。 服务端 
                           
                            | [ServiceContract]public interface IExampleService
 {
 [OperationContract]
 string Method(string name);
 }
 
 public class ExampleService : IExampleService
 {
 public string Method(string name)
 {
 return "Hello " + name;
 }
 }
 
 class Program
 {
 static void Main(string[] args)
 {
 ServiceHost host = new ServiceHost(typeof(ExampleService));
 host.Open();
 Console.ReadKey();
 host.Close();
 }
 }
 
 <configuration>
 <system.serviceModel>
 <services>
 <service name="Example.ExampleService">
 <endpoint address="" binding="wsHttpBinding" contract="Example.IExampleService">
 <identity>
 <dns value="localhost" />
 </identity>
 </endpoint>
 <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
 <host>
 <baseAddresses>
 <add baseAddress="http://localhost:7200/Example/ExampleService/" />
 </baseAddresses>
 </host>
 </service>
 </services>
 </system.serviceModel>
 </configuration>
 |  客户端 
                           
                            | class Program{
 static void Main(string[] args)
 {
 //设置最大线程数
 ThreadPool.SetMaxThreads(1000, 1000);
 ThreadPoolMessage("Start");
 
 //建立服务对象,异步调用服务方法
 ExampleServiceReference.ExampleServiceClient exampleService = new
 ExampleServiceReference.ExampleServiceClient();
 exampleService.BeginMethod("Leslie",new AsyncCallback(AsyncCallbackMethod),
 exampleService);
 Console.ReadKey();
 }
 
 static void AsyncCallbackMethod(IAsyncResult result)
 {
 Thread.Sleep(1000);
 ThreadPoolMessage("Complete");
 ExampleServiceReference.ExampleServiceClient example =
 (ExampleServiceReference.ExampleServiceClient)result.AsyncState;
 string data=example.EndMethod(result);
 Console.WriteLine(data);
 }
 
 //显示线程池现状
 static void ThreadPoolMessage(string data)
 {
 int a, b;
 ThreadPool.GetAvailableThreads(out a, out b);
 string message = string.Format("{0}\n  CurrentThreadId is {1}\n  " +
 "WorkerThreads is:{2}  CompletionPortThreads is :{3}\n",
 data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
 
 Console.WriteLine(message);
 }
 }
 
 <configuration>
 <system.serviceModel>
 <bindings>
 <wsHttpBinding>
 <binding name="WSHttpBinding_IExampleService" closeTimeout="00:01:00"
 openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
 bypassProxyOnLocal="false" transactionFlow="false"
 hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288"
 maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8"
 useDefaultWebProxy="true" allowCookies="false">
 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
 maxBytesPerRead="4096" maxNameTableCharCount="16384" />
 <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
 <security mode="Message">
 <transport clientCredentialType="Windows" proxyCredentialType="None"
 realm="" />
 <message clientCredentialType="Windows" negotiateServiceCredential="true"
 algorithmSuite="Default" />
 </security>
 </binding>
 </wsHttpBinding>
 </bindings>
 <client>
 <endpoint address="http://localhost:7200/Example/ExampleService/"
 binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IExampleService"
 contract="ExampleServiceReference.IExampleService"
 name="WSHttpBinding_IExampleService">
 <identity>
 <dns value="localhost" />
 </identity>
 </endpoint>
 </client>
 </system.serviceModel>
 </configuration>
 |  注意观察运行结果,异步调用服务时,回调函数都是运行于CLR线程池的I/O线程当中。 
 六、异步 SqlCommand 从ADO.NET 2.0开始,SqlCommand就新增了几个异步方法执行SQL命令。相对于同步执行方式,它使主线程不需要等待数据库的返回结果,在使用复杂性查询或批量插入时将有效提高主线程的效率。使用异步SqlCommand的时候,请注意把ConnectionString 
                          的 Asynchronous Processing 设置为 true 。 注意:SqlCommand异步操作的特别之处在于线程并不依赖于CLR线程池,而是由Windows内部提供,这比使用异步委托更有效率。但如果需要使用回调函数的时候,回调函数的线程依然是来自于CLR线程池的工作者线程。 SqlCommand有以下几个方法支持异步操作: 
                           
                            | public IAsyncResult BeginExecuteNonQuery (......)
public int EndExecuteNonQuery(IAsyncResult)
public IAsyncResult BeginExecuteReader(......)
public SqlDataReader EndExecuteReader(IAsyncResult)
public IAsyncResult BeginExecuteXmlReader (......)
public XmlReader EndExecuteXmlReader(IAsyncResult)
 |  由于使用方式相似,此处就以 BeginExecuteNonQuery 为例子,介绍一下异步SqlCommand的使用。首先建立connectionString,注意把Asynchronous 
                          Processing设置为true来启动异步命令,然后把SqlCommand.CommandText设置为 
                          WAITFOR DELAY "0:0:3" 来虚拟数据库操作。再通过BeginExecuteNonQuery启动异步操作,利用轮询方式监测操作情况。最后在操作完成后使用EndExecuteNonQuery完成异步操作。 
                           
                            | class Program{
 //把Asynchronous Processing设置为true
 static string connectionString = "Data Source=LESLIE-PC;Initial Catalog=Business;“+
 "Integrated Security=True;Asynchronous Processing=true";
  static void Main(string[] args){
 //把CLR线程池最大线程数设置为1000
 ThreadPool.SetMaxThreads(1000, 1000);
 ThreadPoolMessage("Start");
  //使用WAITFOR DELAY命令来虚拟操作SqlConnection connection = new SqlConnection(connectionString);
 SqlCommand command = new SqlCommand("WAITFOR 
                                DELAY '0:0:3';", connection);
 connection.Open();
  //启动异步SqlCommand操作,利用轮询方式监测操作IAsyncResult result = command.BeginExecuteNonQuery();
 ThreadPoolMessage("BeginRead");
 while (!result.AsyncWaitHandle.WaitOne(500))
 Console.WriteLine("Main thread do work........");
  //结束异步SqlCommandint count= command.EndExecuteNonQuery(result);
 ThreadPoolMessage("\nCompleted");
 Console.ReadKey();
 }
  //显示线程池现状static void ThreadPoolMessage(string data)
 {
 int a, b;
 ThreadPool.GetAvailableThreads(out a, out b);
 string message = string.Format("{0}\n CurrentThreadId 
                                is {1}\n "+
 "WorkerThreads is:{2} CompletionPortThreads 
                                is :{3}\n",
 data, Thread.CurrentThread.ManagedThreadId, a.ToString(), 
                                b.ToString());
 Console.WriteLine(message);
 }
 }
 |  注意运行结果,SqlCommand的异步执行线程并不属于CLR线程池。 
 如果觉得使用轮询方式过于麻烦,可以使用回调函数,但要注意当调用回调函数时,线程是来自于CLR线程池的工作者线程。 
                           
                            | class Program{
 //把Asynchronous Processing设置为true
 static string connectionString = "Data Source=LESLIE-PC;Initial Catalog=Business;”+
 “Integrated Security=True;Asynchronous Processing=true";
 static void Main(string[] args)
 {
 //把CLR线程池最大线程数设置为1000
 ThreadPool.SetMaxThreads(1000, 1000);
 ThreadPoolMessage("Start");
  //使用WAITFOR DELAY命令来虚拟操作SqlConnection connection = new SqlConnection(connectionString);
 SqlCommand command = new SqlCommand("WAITFOR 
                                DELAY '0:0:3';", connection);
 connection.Open();
 
 //启动异步SqlCommand操作,并把SqlCommand对象传递到回调函数
 IAsyncResult result = command.BeginExecuteNonQuery(
 new AsyncCallback(AsyncCallbackMethod),command);
 Console.ReadKey();
 }
  static void AsyncCallbackMethod(IAsyncResult 
                                result){
 Thread.Sleep(200);
 ThreadPoolMessage("AsyncCallback");
 SqlCommand command = (SqlCommand)result.AsyncState;
 int count=command.EndExecuteNonQuery(result);
 command.Connection.Close();
 }
  //显示线程池现状static void ThreadPoolMessage(string data)
 {
 int a, b;
 ThreadPool.GetAvailableThreads(out a, out b);
 string message = string.Format("{0}\n CurrentThreadId 
                                is {1}\n "+
 "WorkerThreads is:{2} CompletionPortThreads 
                                is :{3}\n",
 data, Thread.CurrentThread.ManagedThreadId, a.ToString(), 
                                b.ToString());
  Console.WriteLine(message);}
 }
 |  运行结果: 
 |