<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_DanielWise</title><subtitle type="text"/><id>http://feed.cnblogs.com/blog/u/79101/rss</id><updated>2012-05-26T02:54:17Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/79101/rss"/><entry><id>http://www.cnblogs.com/danielWise/archive/2012/05/26/2519056.html</id><title type="text">C# 线程手册 第七章 网络和线程 创建一个客户端</title><summary type="text">现在你已经对.NET 中的网络编程有了一个初步的了解，现在我们来实际讨论下本章将要实现的示例程序。这个例子的目的是通过创建一个网络应用程序来让你熟悉线程的使用。这个程序实际上由两个小的Windows 窗体程序组成，一个作为服务端而另外一个作为客户端。我们将使用Visual Studio.NET 来设计实现这些程序。设计目标 我们想创建两个交互程序。第一个是用来从一个数据库表中寻找股票交易数据然后将数据异步地返回给客户端的多线程/多用户股票交易服务程序。第二个是一个通过股票交易号来从服务端查询股票信息的客户端。所有这些都异步执行，客户端用户接口在服务端对请求作出响应时不会卡住。 在.NET F.</summary><published>2012-05-26T02:51:00Z</published><updated>2012-05-26T02:51:00Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><link rel="alternate" href="http://www.cnblogs.com/danielWise/archive/2012/05/26/2519056.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/archive/2012/05/26/2519056.html"/><content type="html">&lt;p&gt;&amp;nbsp; 现在你已经对.NET 中的网络编程有了一个初步的了解，现在我们来实际讨论下本章将要实现的示例程序。这个例子的目的是通过创建一个网络应用程序来让你熟悉线程的使用。这个程序实际上由两个小的Windows 窗体程序组成，一个作为服务端而另外一个作为客户端。我们将使用Visual Studio.NET 来设计实现这些程序。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;设计目标&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 我们想创建两个交互程序。第一个是用来从一个数据库表中寻找股票交易数据然后将数据异步地返回给客户端的多线程/多用户股票交易服务程序。第二个是一个通过股票交易号来从服务端查询股票信息的客户端。所有这些都异步执行，客户端用户接口在服务端对请求作出响应时不会卡住。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在.NET Framework 中有很多方法可以为我们处理异步操作；通过这些方法我们从手动创建并管理线程的工作中解放出来。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 下面第一个列表列举出我们创建应用程序所需要的基本信息：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1. 将会有两个独立存在的程序可以在网络上互相通信&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2. 当向服务端查询股票交易时，客户端的用户接口不会由于网络连接问题而导致卡住或延迟&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3. 服务端应该有能力同时处理多个客户端连接和请求，能够以异步方式和客户端通信&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4. 网络设置必须与应用层隔离开来并且是易于修改的&lt;/p&gt;&lt;p&gt;&amp;nbsp; 为了帮助我们了解程序里典型的用户交互逻辑，我们来看一下UML 图形。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205261050179412.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-5-12 20-29-11" src="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205261050188267.png" alt="2012-5-12 20-29-11" width="405" height="288" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;图 1&lt;/p&gt;&lt;p&gt;&amp;nbsp; 到目前为止，我们从一个很高的起点讨论了一下程序的基本设计指导。如果你和大多数程序员一样，你可能已经等不及要看看代码了。事不宜迟，我们现在就来实际创建这两个程序并同时检查一下我们学过的概念。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;创建程序&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 正如之前提到的，本章的示例程序包含两个独立部分：一个客户端和一个服务端。这两个程序将通过一个特殊的TCP/IP 端口进行通信，可以通过应用程序配置文件修改端口。好了，我们现在就来开始实现程序部分。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;创建客户端程序&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 下面的类图包含了客户端程序的所有代码：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205261050189629.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-5-12 20-33-39" src="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205261050199247.png" alt="2012-5-12 20-33-39" width="182" height="353" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; StockClient 应用程序包含所有客户端程序的代码，比如私有成员变量和方法。我们首先创建一个StockClient Windows 窗体程序。在默认窗体上再创建三个控件；一个名为txtStock 的文本框，一个名为btnGetQuote 的按钮和一个名为lstQuotes 的列表视图控件。然后为这个程序添加一个菜单页，添加包括文件，连接和退出菜单项。最后，要保证除了菜单项意外的所有控件的属性都设置为False; 直到用户连接到服务端才启用这些控件。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205261050253952.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-5-12 20-40-22" src="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205261050308898.png" alt="2012-5-12 20-40-22" width="281" height="296" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 代码如下：&lt;/p&gt;&lt;p&gt;&amp;nbsp; 首先是在StockClient 程序中要用到的一些私有成员变量：&lt;/p&gt;&lt;span style="color: blue;"&gt;private int &lt;/span&gt;mPort;&lt;br/&gt;&lt;span style="color: blue;"&gt;private string &lt;/span&gt;mHostName;&lt;br/&gt;&lt;span style="color: blue;"&gt;private const int &lt;/span&gt;mPacketSize = 1024;&lt;br/&gt;&lt;span style="color: blue;"&gt;private byte&lt;/span&gt;[] mReceivedData = &lt;span style="color: blue;"&gt;new byte&lt;/span&gt;[mPacketSize];&lt;br/&gt;&lt;span style="color: blue;"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;TcpClient &lt;/span&gt;mMyClient;&lt;br/&gt;&lt;span style="color: blue;"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;StringBuilder &lt;/span&gt;mStrBuilder = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;StringBuilder&lt;/span&gt;();&lt;p&gt;&amp;nbsp; 稍后我们再看这些变量，现在我们来修改一下ListView 控件以便于可以保留我们输入的所有股票信息。我们需要它包含六列：Symbol, Price, Change, Bid, Ask 和 Volume. 现在创建一个InitializeStockWindow() 来把这些列添加到ListView 控件中：&lt;/p&gt;&lt;span style="color: blue;"&gt;private void &lt;/span&gt;InitializeStockWindow()&lt;br/&gt;{&lt;br/&gt;    lstQuotes.View = &lt;span style="color: #2b91af;"&gt;View&lt;/span&gt;.Details;&lt;br/&gt;    lstQuotes.Columns.Add(&lt;span style="color: #a31515;"&gt;"Symbol"&lt;/span&gt;, 60, &lt;span style="color: #2b91af;"&gt;HorizontalAlignment&lt;/span&gt;.Left);&lt;br/&gt;    lstQuotes.Columns.Add(&lt;span style="color: #a31515;"&gt;"Price"&lt;/span&gt;, 50, &lt;span style="color: #2b91af;"&gt;HorizontalAlignment&lt;/span&gt;.Left);&lt;br/&gt;    lstQuotes.Columns.Add(&lt;span style="color: #a31515;"&gt;"Change"&lt;/span&gt;, 60, &lt;span style="color: #2b91af;"&gt;HorizontalAlignment&lt;/span&gt;.Left);&lt;br/&gt;    lstQuotes.Columns.Add(&lt;span style="color: #a31515;"&gt;"Bid"&lt;/span&gt;, 50, &lt;span style="color: #2b91af;"&gt;HorizontalAlignment&lt;/span&gt;.Left);&lt;br/&gt;    lstQuotes.Columns.Add(&lt;span style="color: #a31515;"&gt;"Ask"&lt;/span&gt;, 50, &lt;span style="color: #2b91af;"&gt;HorizontalAlignment&lt;/span&gt;.Left);&lt;br/&gt;    lstQuotes.Columns.Add(&lt;span style="color: #a31515;"&gt;"Volume"&lt;/span&gt;, 170, &lt;span style="color: #2b91af;"&gt;HorizontalAlignment&lt;/span&gt;.Left);&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 下面代码用来实现关闭事件。它简单地启用文件菜单的连接选项并通过一个消息提示框提示消息：&lt;/p&gt;&lt;span style="color: blue;"&gt;public delegate void &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;DisconnectedHandler&lt;/span&gt;(&lt;span style="color: blue;"&gt;object &lt;/span&gt;sender);&lt;br/&gt;&lt;span style="color: blue;"&gt;public event &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;DisconnectedHandler &lt;/span&gt;Disconnected;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;private void &lt;/span&gt;OnDisconnected(&lt;span style="color: blue;"&gt;object &lt;/span&gt;sender)&lt;br/&gt;{&lt;br/&gt;    mnuConnect.Enabled = &lt;span style="color: blue;"&gt;true&lt;/span&gt;;&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;MessageBox&lt;/span&gt;.Show(&lt;span style="color: #a31515;"&gt;"The connection was lost!"&lt;/span&gt;, &lt;br/&gt;        &lt;span style="color: #a31515;"&gt;"Disconnected"&lt;/span&gt;, &lt;span style="color: #2b91af;"&gt;MessageBoxButtons&lt;/span&gt;.OK, &lt;span style="color: #2b91af;"&gt;MessageBoxIcon&lt;/span&gt;.Error);&lt;br/&gt;    EnableComponents(&lt;span style="color: blue;"&gt;false&lt;/span&gt;);&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 提到关闭事件，就不得不说一下连接事件，下面看一下连接方法：&lt;/p&gt;&lt;span style="color: blue;"&gt;private void &lt;/span&gt;mnuConnect_Click(&lt;span style="color: blue;"&gt;object &lt;/span&gt;sender, &lt;span style="color: #2b91af;"&gt;EventArgs &lt;/span&gt;e)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;IDictionary &lt;/span&gt;hostSettings;&lt;br/&gt;    &lt;span style="color: blue;"&gt;try&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        hostSettings = (&lt;span style="color: #2b91af;"&gt;IDictionary&lt;/span&gt;)&lt;span style="color: #2b91af;"&gt;ConfigurationManager&lt;/span&gt;.GetSection(&lt;span style="color: #a31515;"&gt;"HostInfo"&lt;/span&gt;);&lt;br/&gt;        mHostName = (&lt;span style="color: blue;"&gt;string&lt;/span&gt;)hostSettings[&lt;span style="color: #a31515;"&gt;"hostname"&lt;/span&gt;];&lt;br/&gt;        mPort = (&lt;span style="color: blue;"&gt;int&lt;/span&gt;)hostSettings[&lt;span style="color: #a31515;"&gt;"port"&lt;/span&gt;];&lt;br/&gt;&lt;br/&gt;        mMyClient = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;TcpClient&lt;/span&gt;(mHostName, mPort);&lt;br/&gt;        mMyClient.GetStream().BeginRead(&lt;br/&gt;            mReceivedData, 0, mPacketSize, &lt;br/&gt;            &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;AsyncCallback&lt;/span&gt;(ReceiveStream), &lt;span style="color: blue;"&gt;null&lt;/span&gt;);&lt;br/&gt;        EnableComponents(&lt;span style="color: blue;"&gt;true&lt;/span&gt;);&lt;br/&gt;        InitializeStockWindow();&lt;br/&gt;        mnuConnect.Enabled = &lt;span style="color: blue;"&gt;false&lt;/span&gt;;&lt;br/&gt;        Disconnected += &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;DisconnectedHandler&lt;/span&gt;(OnDisconnected);&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(System.&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;MessageBox&lt;/span&gt;.Show(&lt;span style="color: #a31515;"&gt;"Error: Unable to establish a connection!"&lt;/span&gt;, &lt;br/&gt;            &lt;span style="color: #a31515;"&gt;"Disconnected"&lt;/span&gt;, &lt;span style="color: #2b91af;"&gt;MessageBoxButtons&lt;/span&gt;.OK, &lt;span style="color: #2b91af;"&gt;MessageBoxIcon&lt;/span&gt;.Error);&lt;br/&gt;        mMyClient.Close();&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 如果你正确地实现了上面所有步骤而且有一个服务器在指定主机名和端口处监听，那么就会创建一个新连接。为了保持住连接，我们必须生成一个后台线程来异步地从服务端获取数据并将数据显示给用户。这部分开始变得有趣了。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 如之前提到的，我们需要我们程序的接收方法是异步的。这是客户端能够工作且不会延迟任何用户请求的唯一方式。让客户端程序在等待数据从服务端返回过程中卡死是无法让人接受的。由于有了.NET Framework, 解决方案相对来说简单并易于实现了。我们首先定义一个TcpClient 的NetworkStream 对象。我们可以调用TcpClinet.GetStream() 方法来返回NetworkStream 对象，并通过它来发送和接收数据。NetworkStream 继承自Stream 类，提供了一系列方法用来进行网络通信。一旦我们有了一个底层数据流，我们可以用它来在网络上发送和接收数据。与其兄弟类FileStream 和 TextStream 类似，NetworkStream 类暴露读写方法用来以同步方式发送和接收数据。BeginRead() 和 BeginWrite() 是这些方法的异步版本。实际上，.NET Framework 中的大部分以Begin开始的方法，比如BeginRead() 和 BeginGetResponse()，当作为委托时不需要程序员提供任何额外代码就能实现异步调用。因此，没有必要生成新线程，由于有一个后台线程来处理数据读取，程序的主线程仍然可以相应UI 请求。让我们来看一下BeginRead() 方法签名：&lt;/p&gt;&lt;span style="color: blue;"&gt;public override &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;IAsyncResult &lt;/span&gt;BeginRead(&lt;br/&gt;    &lt;span style="color: blue;"&gt;byte&lt;/span&gt;[] buffer,&lt;br/&gt;    &lt;span style="color: blue;"&gt;int &lt;/span&gt;offset,&lt;br/&gt;    &lt;span style="color: blue;"&gt;int &lt;/span&gt;size,&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;AsyncCallback &lt;/span&gt;callback,&lt;br/&gt;    &lt;span style="color: blue;"&gt;object &lt;/span&gt;state);&lt;p&gt;&amp;nbsp; 下表解释了这个方法的每个参数。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205261050311896.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-5-26 9-40-45" src="http://images.cnblogs.com/cnblogs_com/danielWise/201205/20120526105031435.png" alt="2012-5-26 9-40-45" width="530" height="136" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在我们继续之前，让我们来了解下异步调用，因为这是一个非常重要的概念。正如之前提到的那样，同步操作的问题在于直到调用结束之后工作线程才能继续工作。异步调用可以运行在一个后台线程中并允许调用线程继续正常执行。.NET 允许使用委托对任何类/方法进行异步调用。然而，特定的类，比如NetworkStream中的BeginRead() 方法已经包含内建的异步能力。委托作为需要进行异步调用的占位符。委托事实上是一个类型安全的函数指针。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 正如我们看到的，BeginRead() 方法需要一个字节数组而不是字符串或者文本流，因此处理起来会稍微有点复杂。我们已经定义了一个名为ReceiveData的变量和另外一个整型变量PacketSize. 现在我们需要传递实际接收数据的方法名-当数据到达以后这个方法将要被委托调用。记住这个方法将要运行在一个后台线程中，所以如果我们希望和UI交互的话那就得小心了。我们通过一行代码生成一个后台线程来接收通过网络从服务端发过来的数据：&lt;/p&gt;mMyClient.GetStream().BeginRead(&lt;br/&gt;    mReceivedData, 0, mPacketSize, &lt;br/&gt;    &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;AsyncCallback&lt;/span&gt;(ReceiveStream), &lt;span style="color: blue;"&gt;null&lt;/span&gt;);&lt;p&gt;&amp;nbsp; 我们创建了一个ReceivedStream() 方法来处理接收到的数据：&lt;/p&gt;&lt;span style="color: blue;"&gt;private void &lt;/span&gt;ReceiveStream(&lt;span style="color: #2b91af;"&gt;IAsyncResult &lt;/span&gt;ar)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;int &lt;/span&gt;bytesCount;&lt;br/&gt;    &lt;span style="color: blue;"&gt;try&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        bytesCount = mMyClient.GetStream().EndRead(ar);&lt;br/&gt;        &lt;span style="color: blue;"&gt;if &lt;/span&gt;(bytesCount &amp;lt; 1)&lt;br/&gt;        {&lt;br/&gt;            Disconnected(&lt;span style="color: blue;"&gt;this&lt;/span&gt;);&lt;br/&gt;            &lt;span style="color: blue;"&gt;return&lt;/span&gt;;&lt;br/&gt;        }&lt;br/&gt;        MesssageAssembler(mReceivedData, 0, bytesCount);&lt;br/&gt;        mMyClient.GetStream().BeginRead(mReceivedData, 0, &lt;br/&gt;            mPacketSize, &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;AsyncCallback&lt;/span&gt;(ReceiveStream), &lt;span style="color: blue;"&gt;null&lt;/span&gt;);&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(System.&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: green;"&gt;//Display error message&lt;br/&gt;        &lt;/span&gt;&lt;span style="color: blue;"&gt;object&lt;/span&gt;[] paramObjs = {(&lt;span style="color: #a31515;"&gt;"An error has occurred " &lt;/span&gt;+ ex.ToString()).ToString()};&lt;br/&gt;        Invoke(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;InvokeDisplay&lt;/span&gt;(DisplayData), paramObjs);&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 首先，我们需要检查下缓存中是否有数据。通常情况下应该一直有数据。你可以把网络连接想象成一段连续的脉冲；只要客户端连着服务端，在到来的数据包中就会有数据，不管多小。我们使用Stream 对象的EndRead() 方法来检查当前字节数组的大小。我们给EndRead() 方法传递一个IAsyncResult 的实例。GetStream().BeginRead() 方法初始化一个异步调用来调用ReceiveStream()方法，为了保证异步调用完成编译器还会在后台做一些额外工作。ReceiveStream() 方法在一个线程池线程上执行。如果委托方法ReceiveStream() 抛出一个异常，那么新创建的异步线程就会被终止，并在调用线程中再产生一个异常。下表深入描述了这种情况：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205261052535161.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-5-26 10-00-18" src="http://images.cnblogs.com/cnblogs_com/danielWise/201205/20120526105329775.png" alt="2012-5-26 10-00-18" width="447" height="403" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 如果EndRead() 方法返回的数字小于1，我们就会知道连接已经丢失，接下来可以引发Disconnected 事件来进行适当的工作处理这种情况。然而，如果接收到字节数目大于0，我们可以开始接受数据。在这个时候，我们需要一个帮助类类帮助我们把从服务端接收到的数据构造成一个字符串。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 事实上，.NET 中你可以按照与BeginRead()方法同样行为来异步调用几乎任何方法。你仅需要定义一个委托并使用BeginInvoke()和EndInvoke() 来调用委托。后者在定义委托时会自动添加。异步架构复杂的细节已经抽象出来，你不需要担心后台线程和同步问题。需要注意的是在VS.NET IDE 的智能感知中不能找到这两个方法。它们仅在运行时才会被添加。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 好的，我们再来看看MessageAssembler() 方法。由于BeginRead() 方法的异步特性，我们实际上并不知道何时以及何种数据将会从服务端到达。数据可能一次全部接收到，或者可能以几百个小数据块形式到达，每块数据仅是一到两个字母大小。在这种情况下，我们将在消息的后面加上一个字符&amp;rdquo;#&amp;rdquo;,这将告诉MessageAssembler() 方法何时到达消息末尾，并停止接受数据以便进一步处理数据。我们将使用StringBuilder - 这个类用来高性能字符串连接操作。我们来看一下MessageAssembler() 方法：&lt;/p&gt;&lt;span style="color: blue;"&gt;private void &lt;/span&gt;MesssageAssembler(&lt;span style="color: blue;"&gt;byte&lt;/span&gt;[] bytes, &lt;span style="color: blue;"&gt;int &lt;/span&gt;offset, &lt;span style="color: blue;"&gt;int &lt;/span&gt;count)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;for &lt;/span&gt;(&lt;span style="color: blue;"&gt;int &lt;/span&gt;bytesCount = 0; bytesCount &amp;lt; count - 1;bytesCount++ )&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: blue;"&gt;if &lt;/span&gt;(bytes[bytesCount] == 35) &lt;span style="color: green;"&gt;//Check for "#" to signal the end&lt;br/&gt;        &lt;/span&gt;{&lt;br/&gt;            &lt;span style="color: blue;"&gt;object&lt;/span&gt;[] paramObjs = &lt;span style="color: blue;"&gt;new object&lt;/span&gt;[]{mStrBuilder.ToString()};&lt;br/&gt;            Invoke(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;InvokeDisplay&lt;/span&gt;(DisplayData), paramObjs);&lt;br/&gt;            mStrBuilder = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;StringBuilder&lt;/span&gt;();&lt;br/&gt;        }&lt;br/&gt;        &lt;span style="color: blue;"&gt;else&lt;br/&gt;        &lt;/span&gt;{&lt;br/&gt;            mStrBuilder.Append((&lt;span style="color: blue;"&gt;char&lt;/span&gt;)bytes[bytesCount]);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 我们可以看到MessageAssembler() 方法循环遍历字节数组并把数据作为一个字符串附加到StringBuilder 实例上知道遇到&amp;rdquo;#&amp;ldquo;字符。一旦遇到这个字符，意味着数据流到达末尾。我们不需要担心字节到字符串的转换因为StringBuilder 类替我们做了这些。接下来它会调用DisplayData() 方法来处理数据：&lt;/p&gt;&lt;span style="color: blue;"&gt;private void &lt;/span&gt;DisplayData(&lt;span style="color: blue;"&gt;string &lt;/span&gt;stockInfo)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;if &lt;/span&gt;(stockInfo == &lt;span style="color: #a31515;"&gt;"-1"&lt;/span&gt;)&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;MessageBox&lt;/span&gt;.Show(&lt;span style="color: #a31515;"&gt;"Symbol not found!"&lt;/span&gt;, &lt;span style="color: #a31515;"&gt;"Invalid Symbol"&lt;/span&gt;, &lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;MessageBoxButtons&lt;/span&gt;.OK, &lt;span style="color: #2b91af;"&gt;MessageBoxIcon&lt;/span&gt;.Error);&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;else&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        AddStock(stockInfo);&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 这是我们第二次看到类似的代码，你可能想知道它是用来干嘛的。这个方法运行在后台工作线程里且与UI表单是同一个线程。尽管我们可以在程序中任何地方调用这个方法，但是这样做并不是一个好主意，因为它是非线程安全的。Windows 窗体程序基于Win32 单线程单元并且是非线程安全的，这意味着一个窗体在初始化以后不能安全地与操作线程(包括异步操作创建的后台线程)之间来回切换。你必须在窗体程序内部调用方法。为了解决这个问题，CLR 支持Invoke() 方法，它负责包装不同线程间的调用。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 如果你怀疑上面说的，你可以自己调试一下代码并通过线程窗口看代码执行的当前线程ID。通过窗体的Invoke()方法调用创建的委托，实际上是运行在窗体线程中的，因此可以与窗体控件进行交互。如果不使用包装，通常情况下代码执行也没有问题，但是以后可能出问题并导致程序执行不稳定。程序生成的线程越多情况就会变得越差。因此，如果没有包装线程的话不要调用GUI。额外的，委托签名必须与Invoke()方法匹配，我们需要创建一个对象数组来保存字符串；这是使用Invoke()的唯一方式。我们可以调用DisplayData()方法来显示数据：&lt;/p&gt;&lt;span style="color: blue;"&gt;object&lt;/span&gt;[] paramObjs = &lt;span style="color: blue;"&gt;new object&lt;/span&gt;[]{mStrBuilder.ToString()};&lt;br/&gt;      Invoke(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;InvokeDisplay&lt;/span&gt;(DisplayData), paramObjs);&lt;p&gt;当要发送的数据传递给Send() 方法，Send() 方法创建一个SteamWriter 类的实例并传递TcpClient 流作为其参数然后调用Write()方法，这回将数据以数据流形式在网络上发送。我们也调用Flush()方法来保证所有数据立即发送出去而不会被放到缓存中：&lt;/p&gt;&lt;span style="color: blue;"&gt;private void &lt;/span&gt;Send(&lt;span style="color: blue;"&gt;string &lt;/span&gt;sendData)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;StreamWriter &lt;/span&gt;writer = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;StreamWriter&lt;/span&gt;(mMyClient.GetStream());&lt;br/&gt;    writer.Write(sendData);&lt;br/&gt;    writer.Flush();&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 我们将要完成了，现在做一些清理工作。大多数据情况，Windows窗体类会调用它自己的Dispose()方法来清理资源，但是由于.NET 有不确定的垃圾回收期，所以我们最好自己手动关闭TcpClient 连接。我们写一个小函数来实现这个：&lt;/p&gt;&lt;span style="color: blue;"&gt;private void &lt;/span&gt;CloseConnection()&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mMyClient != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;br/&gt;    {&lt;br/&gt;        mMyClient.Close();&lt;br/&gt;        mMyClient = &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;至此，整个程序的客户端部分已经完成。下一篇我们将介绍如何创建服务端部分&amp;hellip;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/danielWise/aggbug/2519056.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/danielWise/archive/2012/05/26/2519056.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/danielWise/archive/2012/05/06/2485640.html</id><title type="text">C# 线程手册 第七章 网络和线程</title><summary type="text">概述 在本书的之前章节，我们已经深入地了解了C#.NET 中的线程并探讨了多线程编程中的不同概念和技术。现在你已经是一个线程专家啦，我们将要使用C#实现一个简单的多线程客户端-服务端程序并在其中使用一些我们到目前为止讨论过的概念。 多一些应用来说，高效地使用线程和异步编程是不可或缺的，比如网络通信，高效用户接口以及磁盘输入/输出等等。在所有这些情况下，如果使用单线程的话程序可能会卡死或者在等待一个操作完成的过程中崩溃。对那些网速比较慢的用户来说，一个网络程序的延迟时间是最重要的衡量标准。在本章的示例程序中，我们将要使用System.Net 命名空间并简要地了解下.NET 的网络能力，尤其是对具</summary><published>2012-05-06T01:59:00Z</published><updated>2012-05-06T01:59:00Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><link rel="alternate" href="http://www.cnblogs.com/danielWise/archive/2012/05/06/2485640.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/archive/2012/05/06/2485640.html"/><content type="html">&lt;p&gt;&lt;strong&gt;概述&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在本书的之前章节，我们已经深入地了解了C#.NET 中的线程并探讨了多线程编程中的不同概念和技术。现在你已经是一个线程专家啦，我们将要使用C#实现一个简单的多线程客户端-服务端程序并在其中使用一些我们到目前为止讨论过的概念。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 多一些应用来说，高效地使用线程和异步编程是不可或缺的，比如网络通信，高效用户接口以及磁盘输入/输出等等。在所有这些情况下，如果使用单线程的话程序可能会卡死或者在等待一个操作完成的过程中崩溃。对那些网速比较慢的用户来说，一个网络程序的延迟时间是最重要的衡量标准。在本章的示例程序中，我们将要使用System.Net 命名空间并简要地了解下.NET 的网络能力，尤其是对具有多用户和异步特性的网络程序来说，使用线程是毋庸置疑的。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 我们将讨论以下内容：&lt;/p&gt;&lt;p&gt;&amp;nbsp; 1. 使用System.Net 命名空间开发网络程序&lt;/p&gt;&lt;p&gt;&amp;nbsp; 2. 基于TCP/IP 开发一个简单的客户-服务端程序&lt;/p&gt;&lt;p&gt;&amp;nbsp; 3. 使用.NET 功能实现异步操作&lt;/p&gt;&lt;p&gt;&amp;nbsp; 4. 在一个客户端和一个远程服务端之间使用异步消息传输&lt;/p&gt;&lt;p&gt;&lt;strong&gt;.NET 中的网络&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在.NET Framework 之前, 开发基于Windows 平台的网络程序就仅限于使用难以理解的WinSock 库的C++高级程序员。当然Wintel平台下使用Visual Basic 的开发人员也可以开发出一些相对简单的任务。其他语言提供一些额外的功能。然而，一个开发人员在使用由这些语言提供的简单的、有限的服务时会遇到很多功能障碍，因此不得不进行很多次尝试。&lt;/p&gt;&lt;p&gt;幸运的是，.NET Framework 中的System.Net 命名空间带来了一系列以一种简单的、一致的对象模型包装好的高效功能集合。这些类的易用性并未导致功能的减少，WinSock 2.0 中几乎所有的核心功能都被System.Net命名空间包装和抽象出来。开发人员可以很容易地开发从套接字直到HTTP间任何层级的程序。和原生的WinSock 类不同，System.Net 命名空间将开发人员从自己亲自动手实现很多迫切的资源管理任务的&amp;ldquo;快乐&amp;rdquo;中解放出来，比如处理IO重叠和端口完全性。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 嗯，事不宜迟，我们现在就来看看System.Net 命名空间。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;System.Net 命名空间&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; System.Net 命名空间事实上有两个命名空间组成，System.Net 和 System.Net.Socket.&lt;/p&gt;&lt;p&gt;&amp;nbsp; 我们从System.Net.Socket 命名空间开始。System.Net 命名空间中的类的分层解决方案允许应用程序按照需要使用控件的多种层次来访问网络。除了支持套接字，System.Net 类也提供一个令人印象深刻的功能集合来使用HTTP协议。对大多数情况来说，System.Net 提供三层架构，应用层协议，传输层协议和网络协议。System.Net.Sockets 命名空间由很多初始类和实现组成，可以用来处理传输协议。让我们看看System.Net 命名空间中的很多重要的类，如下表所示：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205060958275494.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-5-6 9-34-19" src="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205060958283708.png" alt="2012-5-6 9-34-19" width="492" height="456" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 你可以看到，System.Net 命名空间包含了很多对站点和网络编程非常有用的类和实现。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;System.Net.Sockets 命名空间&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; System.Net.Sockets 命名空间关注传输层：套接字包含了一系列复杂的类。这些类在抽象与套接字编程过程相关的复杂任务中做了出色的工作，它们提供了一个有力且高效的套接字堆栈并向下兼容伯克利套接字。最后，对TCP和UDP的内建支持被很好地集成在System.Net.Sockets 类中。下表列出了System.Net.Sockets命名空间中的类。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205060958296838.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-5-6 9-53-35" src="http://images.cnblogs.com/cnblogs_com/danielWise/201205/201205060958301364.png" alt="2012-5-6 9-53-35" width="496" height="218" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 一个多种不同层次的控件提供给了开发人员，比如用于系统底层的Socket 类，用于系统上层的TcpClient类，它们提供了易于使用的特性和更多功能。更多内容已经超出了本书的范畴(可以参考《专业.NET 网络编程》)，这里我们仅详细地看一下上面的这些类，因为我们稍后要使用它们设计并开发我们的示例程序。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;下一篇通过一个示例程序介绍C# 线程手册 中所囊括的所有内容&amp;hellip;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/danielWise/aggbug/2485640.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/danielWise/archive/2012/05/06/2485640.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/danielWise/archive/2012/04/28/2475024.html</id><title type="text">C# 线程手册 第六章 线程调试与跟踪 DataImport 例子</title><summary type="text">现在我们要集中精力实现一个实战实例来描述到目前为止我们已经看过的内容。这里要实现的DataImport 例子是那种等待文件到达指定目录然后将其导入到一个SQL Server 数据库中的典型程序。下面我们列出了这个例子中将要使用的类： FileSystemWatcher: 这个类允许开发人员监控指定目录并能够在发生改变时(比如创建一个新文件或者删除一个文件)触发事件。这个类位于System.IO 命名空间中。 TextWriterTraceListener: 实现我们自己的跟踪功能。 Thread: 已经看过很多遍了，允许我们启动一个新线程来把数据导入到数据库中。 很多SqlClient 命名.</summary><published>2012-04-28T06:01:00Z</published><updated>2012-04-28T06:01:00Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><link rel="alternate" href="http://www.cnblogs.com/danielWise/archive/2012/04/28/2475024.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/archive/2012/04/28/2475024.html"/><content type="html">&lt;p&gt;&amp;nbsp; 现在我们要集中精力实现一个实战实例来描述到目前为止我们已经看过的内容。这里要实现的DataImport 例子是那种等待文件到达指定目录然后将其导入到一个SQL Server 数据库中的典型程序。下面我们列出了这个例子中将要使用的类：&lt;/p&gt;&lt;p&gt;&amp;nbsp; FileSystemWatcher: 这个类允许开发人员监控指定目录并能够在发生改变时(比如创建一个新文件或者删除一个文件)触发事件。这个类位于System.IO 命名空间中。&lt;/p&gt;&lt;p&gt;&amp;nbsp; TextWriterTraceListener: 实现我们自己的跟踪功能。&lt;/p&gt;&lt;p&gt;&amp;nbsp; Thread: 已经看过很多遍了，允许我们启动一个新线程来把数据导入到数据库中。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 很多SqlClient 命名空间里的类，用于管理SQL Server 数据库连接和更新。&lt;/p&gt;&lt;p&gt;第一版的DataImport 故意加了点逻辑错误，这样你就可以使用跟踪功能的日志文件并了解其重要性。&lt;/p&gt;&lt;p&gt;代码如下：&lt;/p&gt;&lt;span style="color: green;"&gt;/*************************************&lt;br/&gt;/* Copyright (c) 2012 Daniel Dong&lt;br/&gt; * &lt;br/&gt; * Author：oDaniel Dong&lt;br/&gt; * Blog：o  www.cnblogs.com/danielWise&lt;br/&gt; * Email：o guofoo@163.com&lt;br/&gt; * &lt;br/&gt; */&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Linq;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Text;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Diagnostics;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.IO;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Threading;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;namespace &lt;/span&gt;DataImport&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;DataImport&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;BooleanSwitch &lt;/span&gt;bs;&lt;br/&gt;        &lt;span style="color: blue;"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;FileSystemWatcher &lt;/span&gt;fsw;&lt;br/&gt;&lt;br/&gt;        [&lt;span style="color: #2b91af;"&gt;STAThread&lt;/span&gt;]&lt;br/&gt;        &lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: green;"&gt;//Remove the default listener&lt;br/&gt;            &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Listeners.RemoveAt(0);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: green;"&gt;//Create and add the new listener&lt;br/&gt;            &lt;/span&gt;bs = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;BooleanSwitch&lt;/span&gt;(&lt;span style="color: #a31515;"&gt;"DISwitch"&lt;/span&gt;, &lt;span style="color: #a31515;"&gt;"DataImport switch"&lt;/span&gt;);&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Listeners.Add(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;TextWriterTraceListener&lt;/span&gt;(&lt;br/&gt;                &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;FileStream&lt;/span&gt;(&lt;span style="color: #a31515;"&gt;@"D:\DataImport.log"&lt;/span&gt;, &lt;span style="color: #2b91af;"&gt;FileMode&lt;/span&gt;.OpenOrCreate)));&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: green;"&gt;//Create a FileSystemWatcher object used to monitor the specified directory&lt;br/&gt;            &lt;/span&gt;fsw = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;FileSystemWatcher&lt;/span&gt;();&lt;br/&gt;            fsw.Path = &lt;span style="color: #a31515;"&gt;@"D:\Temp"&lt;/span&gt;;&lt;br/&gt;            fsw.Filter = &lt;span style="color: #a31515;"&gt;"*.xml"&lt;/span&gt;;&lt;br/&gt;            fsw.IncludeSubdirectories = &lt;span style="color: blue;"&gt;false&lt;/span&gt;;&lt;br/&gt;            fsw.Created += &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;FileSystemEventHandler&lt;/span&gt;(OnFileCreated);&lt;br/&gt;            fsw.EnableRaisingEvents = &lt;span style="color: blue;"&gt;true&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: blue;"&gt;try&lt;br/&gt;            &lt;/span&gt;{&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;WaitForChangedResult &lt;/span&gt;res;&lt;br/&gt;                &lt;span style="color: blue;"&gt;while &lt;/span&gt;(&lt;span style="color: blue;"&gt;true&lt;/span&gt;)&lt;br/&gt;                {&lt;br/&gt;                    res = fsw.WaitForChanged(&lt;span style="color: #2b91af;"&gt;WatcherChangeTypes&lt;/span&gt;.Created);&lt;br/&gt;                    &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(bs.Enabled, &lt;br/&gt;                        &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Found: " &lt;/span&gt;+ res.Name + &lt;span style="color: #a31515;"&gt;" file"&lt;/span&gt;);&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;            &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(bs.Enabled, &lt;br/&gt;                    &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- An exception occured while waiting for file :"&lt;/span&gt;);&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Indent();&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(bs.Enabled, &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- " &lt;/span&gt;+ ex.ToString());&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Unindent();&lt;br/&gt;            }&lt;br/&gt;            &lt;span style="color: blue;"&gt;finally&lt;br/&gt;            &lt;/span&gt;{&lt;br/&gt;                fsw.EnableRaisingEvents = &lt;span style="color: blue;"&gt;false&lt;/span&gt;;&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(bs.Enabled, &lt;br/&gt;                    &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Directory monitoring stopped"&lt;/span&gt;);&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Close();&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;static void &lt;/span&gt;OnFileCreated(&lt;span style="color: blue;"&gt;object &lt;/span&gt;sender, &lt;span style="color: #2b91af;"&gt;FileSystemEventArgs &lt;/span&gt;e)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: blue;"&gt;try&lt;br/&gt;            &lt;/span&gt;{&lt;br/&gt;                &lt;span style="color: green;"&gt;//Create a new object from the ImportData class to process the incoming file&lt;br/&gt;                &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ImportData &lt;/span&gt;id = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ImportData&lt;/span&gt;();&lt;br/&gt;                id.mStrFileName = e.FullPath;&lt;br/&gt;&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Thread &lt;/span&gt;t = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadStart&lt;/span&gt;(id.Import));&lt;br/&gt;                t.Name = &lt;span style="color: #a31515;"&gt;"DataImportThread"&lt;/span&gt;;&lt;br/&gt;                t.Start();&lt;br/&gt;            }&lt;br/&gt;            &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(bs.Enabled, &lt;br/&gt;                    &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- An exception occured while queuing file:"&lt;/span&gt;);&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Indent();&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(bs.Enabled, &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- " &lt;/span&gt;+ ex.ToString());&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Unindent();&lt;br/&gt;            }&lt;br/&gt;            &lt;span style="color: blue;"&gt;finally&lt;br/&gt;            &lt;/span&gt;{&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Flush();&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&lt;strong&gt;测试程序&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;按照以下步骤测试程序：&lt;/p&gt;&lt;p&gt;&amp;nbsp; 创建一个C:\Temp 目录来存储XML 文件&lt;/p&gt;&lt;p&gt;&amp;nbsp; 运行DataImport 程序&lt;/p&gt;&lt;p&gt;&amp;nbsp; 把authors.xml 文件放到C:\Temp 目录中&lt;/p&gt;&lt;p&gt;最终结果可以从DataImport.log 文件中找到，结果如下：&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;2012/4/28 13:41:20- Found: Authors.xml file &lt;br /&gt;2012/4/28 13:41:20- Filling the DataSet    &lt;br /&gt;2012/4/28 13:41:20- Reading XML file    &lt;br /&gt;2012/4/28 13:41:20- DatSet filled with data.    &lt;br /&gt;2012/4/28 13:41:20- Updating database...    &lt;br /&gt;2012/4/28 13:41:20- Database updated successfully.    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2012/4/28 13:41:23- Total TIME: 00:00:02.0370000 seconds&lt;/span&gt;  &lt;/p&gt;&lt;p&gt;&amp;nbsp; authors.xml 文件不大，整个运行时间非常短。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;逻辑错误&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 看起来所有的都按照期望的那样运行了，但是还有些情况没有覆盖到。到目前为止，我们使用小文件测试程序，当程序接收到文件创建事件后会打开文件，然后把文件拷贝到指定目录中并关闭文件。如果我们接收一个大文件会出现什么情况？结果会是当线程尝试访问XML文件并填充DataSet 对象时，它会接收到一个尝试打开正在使用的文件异常。现在我们拷贝huge_authors.xml 文件来再测试一遍程序。由于我们使用了跟踪功能，你可以在日志文件中找到以下错误：&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;2012/4/28 13:53:14- Found: Authors.xml file    &lt;br /&gt;2012/4/28 13:53:14- Filling the DataSet    &lt;br /&gt;2012/4/28 13:53:14- Reading XML file    &lt;br /&gt;2012/4/28 13:53:22- A general exception occured during file processing:     &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2012/4/28 13:53:22- System.IO.IOException:     &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; The process cannot access the file 'D:\Temp\Authors.xml' because it is being used by another process.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 对这种类型的错误调试器通常捕获不到，因为用来运行程序的时间和调试到代码的时间足够用来拷贝一个文件。在你的环境中也不一定发生，发生这个问题关键在于你的硬盘访问速度以及内存大小。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在我们调用ReadXml() 方法之前，你应该尝试显示打开文件。如果发生了一个错误，那么你可以阻塞线程几秒，然后在文件可以处理时再尝试一次。让我们看看DataImport2 是如何实现的，添加了一个GetFileAccess() 方法：&lt;/p&gt;&lt;span style="color: blue;"&gt;private bool &lt;/span&gt;GetFileAccess()&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Trying to get exclusive access to the " &lt;/span&gt;+ mStrFileName + &lt;span style="color: #a31515;"&gt;" file."&lt;/span&gt;);&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;FileStream &lt;/span&gt;fs = &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;br/&gt;    &lt;span style="color: blue;"&gt;try&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        fs = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;FileStream&lt;/span&gt;(mStrFileName, &lt;span style="color: #2b91af;"&gt;FileMode&lt;/span&gt;.Append, &lt;span style="color: #2b91af;"&gt;FileAccess&lt;/span&gt;.Write, &lt;span style="color: #2b91af;"&gt;FileShare&lt;/span&gt;.None);&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Access to the " &lt;/span&gt;+ mStrFileName + &lt;span style="color: #a31515;"&gt;" file allowed."&lt;/span&gt;);&lt;br/&gt;        &lt;span style="color: blue;"&gt;return true&lt;/span&gt;;&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;catch&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Access denied to the " &lt;/span&gt;+ mStrFileName + &lt;span style="color: #a31515;"&gt;" file."&lt;/span&gt;);&lt;br/&gt;        &lt;span style="color: blue;"&gt;return false&lt;/span&gt;;&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;finally&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;if &lt;/span&gt;(fs != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;br/&gt;        {&lt;br/&gt;            fs.Close();&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; GetFileAccess()方法会返回一个布尔值来指示是否可以访问指定文件。这个方法将共享访问属性设置为空并尝试打开文件：&lt;/p&gt;&lt;span style="color: blue;"&gt;public void &lt;/span&gt;Import()&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;string &lt;/span&gt;connectionStr = &lt;span style="color: #a31515;"&gt;@"Data Source=.\SQLEXPRESS;Initial Catalog=pubs;Integrated Security=True"&lt;/span&gt;;&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;SqlConnection &lt;/span&gt;dbConn = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;SqlConnection&lt;/span&gt;(connectionStr);&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;SqlDataAdapter &lt;/span&gt;da = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;SqlDataAdapter&lt;/span&gt;(&lt;span style="color: #a31515;"&gt;"SELECT * FROM authors"&lt;/span&gt;, dbConn);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;DataSet &lt;/span&gt;ds = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;DataSet&lt;/span&gt;();&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;SqlCommandBuilder &lt;/span&gt;sa = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;SqlCommandBuilder&lt;/span&gt;(da);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: blue;"&gt;try&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;while &lt;/span&gt;(!GetFileAccess())&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;.Sleep(5000);&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Trying to access to the " &lt;/span&gt;+ mStrFileName + &lt;span style="color: #a31515;"&gt;" file again."&lt;/span&gt;);&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Filling the DataSet"&lt;/span&gt;);&lt;br/&gt;        da.Fill(ds);&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;DataSet &lt;/span&gt;dsMerge = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;DataSet&lt;/span&gt;();&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Reading XML file"&lt;/span&gt;);&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;.Sleep(8000);&lt;br/&gt;        dsMerge.ReadXml(mStrFileName, &lt;span style="color: #2b91af;"&gt;XmlReadMode&lt;/span&gt;.InferSchema);&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- DatSet filled with data."&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;DateTime &lt;/span&gt;time = &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now;&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, time + &lt;span style="color: #a31515;"&gt;"- Updating database..."&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;        da.TableMappings.Add(&lt;span style="color: #a31515;"&gt;"Table"&lt;/span&gt;, &lt;span style="color: #a31515;"&gt;"authors"&lt;/span&gt;);&lt;br/&gt;        da.InsertCommand = sa.GetInsertCommand();&lt;br/&gt;        da.Update(dsMerge);&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;DateTime &lt;/span&gt;time2 = &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now;&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, time + &lt;span style="color: #a31515;"&gt;"- Database updated successfully."&lt;/span&gt;);&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Indent();&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Total TIME: " &lt;/span&gt;+ time2.Subtract(time) + &lt;span style="color: #a31515;"&gt;" seconds"&lt;/span&gt;);&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Unindent();&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;SqlException &lt;/span&gt;ex)&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- A SQL exception occured during file processing: "&lt;/span&gt;);&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Indent();&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- " &lt;/span&gt;+ ex.ToString());&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Unindent();&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;e)&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- A general exception occured during file processing: "&lt;/span&gt;);&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Indent();&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLineIf(&lt;span style="color: #2b91af;"&gt;DataImport&lt;/span&gt;.bs.Enabled, &lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- " &lt;/span&gt;+ e.ToString());&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Unindent();&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;finally&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Flush();&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; ImportData.Import() 方法显式访问文件。如果文件仍然被拷贝任务使用，线程会阻塞5秒。直到源文件可以被打开时才可以调用GetFileAccess() 方法。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 到目前为止我们已经通过实用例子了解了跟踪功能对在程序运行阶段了解其行为是很重要的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在这一章我们已经了解如何使用Visual Studio.NET 调试器来观察一个程序在执行过程中的行为。同时我们也学习使用调试器提供的允许我们检查并改变一个变量值的强大工具，等等。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在本章的第二部分，我们使用.NET 提供的三个类来讲解跟踪功能：Trace, Debug 和 Switch. 我们通过在应用程序配置文件中修改值来动态地激活/反激活跟踪功能。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 最后，我们通过一个实例学习了帮助开发人员找到并修改问题和逻辑错误的跟踪技术。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;下一篇介绍 第七章 网络和线程&amp;hellip;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/danielWise/aggbug/2475024.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/danielWise/archive/2012/04/28/2475024.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/danielWise/archive/2012/04/25/2470562.html</id><title type="text">C# 线程手册 第六章 线程调试与跟踪 跟踪开关</title><summary type="text">当你的程序接近部署阶段时，你可能想从代码中去掉所有的跟踪和调试消息。然而，你不得不从代码中一条一条地寻找调试指令并将其去掉。对于这种情况你可以在程序编译期间使用编译标志处理。从Visual Studio.NET IDE, 你可以在解决方案管理窗口右键工程名-&gt;从菜单页选择属性。如下图对话框显示： 你只需要简单地将定义调试常量和跟踪常量选项勾掉，重新编译解决方案，所有的跟踪和调试代码都将会被从应用程序中被去掉。 为了去掉跟踪功能，你可以使用csc.exe 命令行编译器。在编译时使用/d:TRACE=FALSE /d:DEBUG=FALSE 开关。 对跟踪代码添加开关功能...</summary><published>2012-04-25T13:41:00Z</published><updated>2012-04-25T13:41:00Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><link rel="alternate" href="http://www.cnblogs.com/danielWise/archive/2012/04/25/2470562.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/archive/2012/04/25/2470562.html"/><content type="html">&lt;p&gt;&amp;#160; 当你的程序接近部署阶段时，你可能想从代码中去掉所有的跟踪和调试消息。然而，你不得不从代码中一条一条地寻找调试指令并将其去掉。对于这种情况你可以在程序编译期间使用编译标志处理。从Visual Studio.NET IDE, 你可以在解决方案管理窗口右键工程名-&amp;gt;从菜单页选择属性。如下图对话框显示：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204252141255086.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="2012-4-25 20-06-19" border="0" alt="2012-4-25 20-06-19" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204252141264313.png" width="542" height="386" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160; 你只需要简单地将定义调试常量和跟踪常量选项勾掉，重新编译解决方案，所有的跟踪和调试代码都将会被从应用程序中被去掉。&lt;/p&gt;  &lt;p&gt;&amp;#160; 为了去掉跟踪功能，你可以使用csc.exe 命令行编译器。在编译时使用/d:TRACE=FALSE /d:DEBUG=FALSE 开关。&lt;/p&gt;  &lt;p&gt;&amp;#160; 对跟踪代码添加开关功能允许我们在运行时激活/反激活跟踪消息。通过简单地在我们程序的配置文件中定义一个值，你就可以激活跟踪功能而不用重新编译代码。通常情况下，为了维持跟踪信息你需要将其集成到应用程序中，结果就是程序变得越来越大、越来越慢，即便跟踪开关已经被关闭。&lt;/p&gt;  &lt;p&gt;&amp;#160; BooleanSwitch 和 TraceSwitch 类是.NET Framework 提供来是实现这些开关的。让我们先来看看BooleanSwitch 类。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;BooleanSwitch 类&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160; 在跟踪代码中使用这个类，你可以通过简单地改一下配置文件来激活/反激活消息。WriteLineIf() 和 WriteIf() 方法根据BooleanSwitch 类的Enabled 属性决定是否写消息。为了在你的程序中添加开关你需要按以下步骤执行：&lt;/p&gt;  &lt;p&gt;&amp;#160; 1. 手动添加一个配置文件或者通过在项目菜单添加一个新文件来添加App.config 文件。&lt;/p&gt;  &lt;p&gt;&amp;#160; 2. 打开配置文件添加如下信息。0 表示禁用跟踪功能，1 表示激活跟踪功能。&lt;/p&gt;  &lt;span style="color: blue"&gt;&amp;lt;?&lt;/span&gt;&lt;span style="color: #a31515"&gt;xml &lt;/span&gt;&lt;span style="color: red"&gt;version&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;1.0&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;encoding&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;utf-8&lt;/span&gt;&amp;quot; &lt;span style="color: blue"&gt;?&amp;gt;&lt;br/&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;configuration&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;br/&gt;  &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;system.diagnostics&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;br/&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;switches&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;br/&gt;      &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;add &lt;/span&gt;&lt;span style="color: red"&gt;name&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;MySwitch&lt;/span&gt;&amp;quot; &lt;span style="color: red"&gt;value &lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;1&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;/&amp;gt;&lt;br/&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;switches&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;br/&gt;  &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;system.diagnostics&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;br/&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;configuration&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;br/&gt;&lt;/span&gt;&lt;p&gt;&amp;#160; 3. 在代码中创建一个新的BooleanSwitch 对象并设置其名字与配置文件中的一样。在下面的代码中我们定义一个全局BooleanSwitch对象：&lt;/p&gt;&lt;span style="color: blue"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;BooleanSwitch &lt;/span&gt;bs;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: green"&gt;//Create a Boolean switch called MySwitch&lt;br/&gt;    &lt;/span&gt;bs = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;BooleanSwitch&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;MySwitch&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;Enable/Disable tracing functionalites&amp;quot;&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green"&gt;//Create a file listener&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af"&gt;FileStream &lt;/span&gt;fs = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;FileStream&lt;/span&gt;(&lt;span style="color: #a31515"&gt;@&amp;quot;D:\Debugging.log&amp;quot;&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;FileMode&lt;/span&gt;.OpenOrCreate);&lt;br/&gt;    &lt;span style="color: #2b91af"&gt;Trace&lt;/span&gt;.Listeners.Add(&lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TextWriterTraceListener&lt;/span&gt;(fs));&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green"&gt;//Write the line only when the switch is on&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Trace&lt;/span&gt;.WriteLineIf(bs.Enabled, &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515"&gt;&amp;quot;- Entered in Main()&amp;quot;&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;}&lt;p&gt;&amp;#160; 在Main() 方法中我们创建了对象，并使用与配置文件中相同的名字对其初始化。WriteLineIf() 方法仅当配置文件中的Enabled 设置为1 时才写消息。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;TraceSwitch 类&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;#160; 这个类是BooleanSwitch 类的高级版本因为它允许我们选择是否反激活跟踪功能或者按照不同重要层次来显示信息。下表列出了不同等级：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204252141275774.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="2012-4-25 20-58-32" border="0" alt="2012-4-25 20-58-32" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204252141273889.png" width="506" height="182" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;#160; 所以，当发生了一个错误时，你可以改变应用程序配置文件，来设定只向监听程序写入你加到代码中的错误信息，这样可以仅关注这些信息。配置文件的设置与BooleanSwitch 例子相同。代码中的改变是我们需要从TraceSwitch 类来实例化一个对象。还有我们将在类中使用枚举来确定跟踪消息的层次。现在来看一个例子，TraceSwitchExample.cs:&lt;/p&gt;&lt;span style="color: blue"&gt;static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TraceSwitch &lt;/span&gt;ts;&lt;br/&gt;&lt;br/&gt;[&lt;span style="color: #2b91af"&gt;STAThread&lt;/span&gt;]&lt;br/&gt;&lt;span style="color: blue"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: green"&gt;//Create a BooleTraceSwitchan switch called MySwitch&lt;br/&gt;    &lt;/span&gt;ts = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TraceSwitch&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;MySwitch&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;Four different trace levels&amp;quot;&lt;/span&gt;);&lt;br/&gt;    &lt;span style="color: green"&gt;//Create a file listener&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af"&gt;FileStream &lt;/span&gt;fs = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;FileStream&lt;/span&gt;(&lt;span style="color: #a31515"&gt;@&amp;quot;D:\Debugging.log&amp;quot;&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;FileMode&lt;/span&gt;.OpenOrCreate);&lt;br/&gt;    &lt;span style="color: #2b91af"&gt;Trace&lt;/span&gt;.Listeners.Add(&lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TextWriterTraceListener&lt;/span&gt;(fs));&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green"&gt;//Write the line only when the switch is on&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Trace&lt;/span&gt;.WriteLineIf(ts.TraceInfo, &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515"&gt;&amp;quot;- Entered in Main()&amp;quot;&lt;/span&gt;);;&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green"&gt;//Create a thread&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Thread &lt;/span&gt;t = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Thread&lt;/span&gt;(&lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ThreadStart&lt;/span&gt;(DBThread));&lt;br/&gt;    t.Start();&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.ReadLine();&lt;br/&gt;    &lt;span style="color: #2b91af"&gt;Trace&lt;/span&gt;.Close();&lt;br/&gt;}&lt;p&gt;&amp;#160; 我们定义了一个全局TraceSwitch 对象然后通过配置文件中的名字生成一个新的实例。我们添加一个文本监听器到程序中。然后启动一个新线程来连接数据库获得Region 表中的所有信息。&lt;/p&gt;&lt;p&gt;&amp;#160; 如果线程已经启动而且Open() 方法产生一个异常时将会产生一条错误消息：&lt;/p&gt;&lt;span style="color: blue"&gt;private static void &lt;/span&gt;DBThread()&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: #2b91af"&gt;Trace&lt;/span&gt;.WriteLineIf(ts.TraceInfo, &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515"&gt;&amp;quot;- Entered in DBThread()&amp;quot;&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green"&gt;//Create a connection object&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlConnection &lt;/span&gt;dbConn = &lt;br/&gt;        &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlConnection&lt;/span&gt;(&lt;span style="color: #a31515"&gt;@&amp;quot;Data Source=DANIELFORWARD\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True&amp;quot;&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green"&gt;//Create a command object to execute a SQL statement&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlCommand &lt;/span&gt;dbComm = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SqlCommand&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;SELECT * FROM Region&amp;quot;&lt;/span&gt;, dbConn);&lt;br/&gt;    &lt;span style="color: #2b91af"&gt;SqlDataReader &lt;/span&gt;dr = &lt;span style="color: blue"&gt;null&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: blue"&gt;try&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: #2b91af"&gt;Trace&lt;/span&gt;.WriteLineIf(ts.TraceInfo, &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515"&gt;&amp;quot;- Execute SQL statement&amp;quot;&lt;/span&gt;);&lt;br/&gt;        &lt;span style="color: green"&gt;//Open the connection to the database&lt;br/&gt;        &lt;/span&gt;dbConn.Open();&lt;br/&gt;        &lt;span style="color: green"&gt;//Execute the SQL statement&lt;br/&gt;        &lt;/span&gt;dr = dbComm.ExecuteReader(System.Data.&lt;span style="color: #2b91af"&gt;CommandBehavior&lt;/span&gt;.CloseConnection);&lt;br/&gt;        &lt;span style="color: blue"&gt;while &lt;/span&gt;(dr.Read())&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: green"&gt;//Reading records&lt;br/&gt;            &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Trace&lt;/span&gt;.WriteLine(dr[0].ToString());&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: green"&gt;//Log the error to the Trace application&lt;br/&gt;        &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Trace&lt;/span&gt;.WriteLineIf(ts.TraceError, &lt;span style="color: #2b91af"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515"&gt;&amp;quot; An error occured in database access, details: &amp;quot; &lt;/span&gt;+ ex.Message);&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue"&gt;finally&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue"&gt;if &lt;/span&gt;(!dr.IsClosed &amp;amp;&amp;amp; dr != &lt;span style="color: blue"&gt;null&lt;/span&gt;)&lt;br/&gt;        {&lt;br/&gt;            dr.Close();&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;#160; 当在配置文件中设定值为1时，TraceError 信息的输出结果如下：&lt;/p&gt;&lt;p&gt;&lt;em&gt;2012/4/25 21:35:29 An error occured in database access, details: 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。    &lt;br /&gt;未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 (provider: SQL 网络接口, error: 26 - 定位指定的服务器/实例时出错)&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&amp;#160; 当配置文件中设定值为3时，TraceInformation 信息的输出结果如下：&lt;/p&gt;&lt;p&gt;&lt;em&gt;2012/4/25 21:37:10- Entered in Main()    &lt;br /&gt;2012/4/25 21:37:12- Entered in DBThread()    &lt;br /&gt;2012/4/25 21:37:12- Execute SQL statement    &lt;br /&gt;2012/4/25 21:37:27 An error occured in database access, details: 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。    &lt;br /&gt;未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 (provider: SQL 网络接口, error: 26 - 定位指定的服务器/实例时出错)&lt;/em&gt;  &lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Debug 类&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;#160; Debug 类提供与Trace 类同样的功能。当你改变Trace 类提供的监听器集合中的监听器程序时，你可以在Debug 类中做同样的事情。&lt;/p&gt;&lt;p&gt;&amp;#160; 这两个类最大的不同在于应用的不同场景。Debug 类用于调试阶段添加信息。在部署我们的应用程序之前，你将创建一个发布版本同时自动去除所有调试信息。因此，你可以在程序运行阶段需要的时候添加Trace 类功能。&lt;/p&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;下一篇介绍一个DataImport 的例子…&lt;/p&gt;&lt;img src="http://www.cnblogs.com/danielWise/aggbug/2470562.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/danielWise/archive/2012/04/25/2470562.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/danielWise/archive/2012/04/24/2468989.html</id><title type="text">C# 线程手册 第六章 线程调试与跟踪 使用不同的监听器程序</title><summary type="text">在这部分，我们将了解如何改变默认的监听器程序。Trace 类和Debug类暴露了监听器集合(一系列监听程序集合)。如果不添加任何新的监听类，DefaultTraceListener 将指向由Visual Studio.NET 提供的调试输出窗口。然而，.NET Framework 提供另外可以用作监听程序的两个类： 如果你需要跟踪一个不在Visual Studio 中执行的多线程应用程序的行为，那么你需要将默认监听器改成以上列表中的某一个。通常情况下，调试输出窗口仅在调试过程中可用。使用这两个类，你可以选择将跟踪消息写入Windows 事件日志中或者一个文本文件中。一般来说，如果你知道你的程.</summary><published>2012-04-24T14:26:00Z</published><updated>2012-04-24T14:26:00Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><link rel="alternate" href="http://www.cnblogs.com/danielWise/archive/2012/04/24/2468989.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/archive/2012/04/24/2468989.html"/><content type="html">&lt;p&gt;&amp;nbsp; 在这部分，我们将了解如何改变默认的监听器程序。Trace 类和Debug类暴露了监听器集合(一系列监听程序集合)。如果不添加任何新的监听类，DefaultTraceListener 将指向由Visual Studio.NET 提供的调试输出窗口。然而，.NET Framework 提供另外可以用作监听程序的两个类：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204242225549660.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-24 21-22-30" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/20120424222558929.png" alt="2012-4-24 21-22-30" width="540" height="111" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 如果你需要跟踪一个不在Visual Studio 中执行的多线程应用程序的行为，那么你需要将默认监听器改成以上列表中的某一个。通常情况下，调试输出窗口仅在调试过程中可用。使用这两个类，你可以选择将跟踪消息写入Windows 事件日志中或者一个文本文件中。一般来说，如果你知道你的程序运行在一个有事件日志的操作系统中的话，那么使用EventLogTraceListener 类将是最好的选择。原因如下：&lt;/p&gt;&lt;p&gt;&amp;nbsp; 1. 事件日志由操作系统管理；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 2. 事件日志允许管理员确定日志的安全设置；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 3. 事件日志需要使用事件日志查看器读取。这比用Notepad 读取的文本文件在阅读效果上要好得多(随着科技进步，也不尽然)。&lt;/p&gt;&lt;p&gt;改变默认的监听器程序很简单，我们来看一个例子，TraceEventLog.cs:&lt;/p&gt;&lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: green;"&gt;//Create a trace listener for the event log.&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;EventLogTraceListener &lt;/span&gt;eltl = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;EventLogTraceListener&lt;/span&gt;(&lt;span style="color: #a31515;"&gt;"TraceLog"&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green;"&gt;//Add the event log trace listener to the collection.&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Listeners.Add(eltl);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green;"&gt;//Write output to the event log.&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Entered in Main()"&lt;/span&gt;);&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 首先，我们需要创建一个新的监听器对象。在上面的例子中，为了将Windows 事件日志作为监听器程序我们创建了一个新的EventLogTraceListener 对象。这个类的构造函数接收一个字符串作为参数，用于设置写入记录的日志源名称。构造函数将会实例化一个新的EventLog 对象并自动地使用输入参数值设置EventLog 类的Source 属性。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 下一步是使用Add() 方法向集合中添加一个新的监听器对象并向监听器对象提供其引用。最后，我们可以开始写入跟踪消息，这些消息将被中转给监听程序。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 使用事件日志查看器(开始菜单-&amp;gt;运行-&amp;gt;输入 eventvwr)打开Windows 事务日志, 你将在应用程序部分看到一条新的日志：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204242225599043.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-24 21-39-50" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204242226016209.png" alt="2012-4-24 21-39-50" width="590" height="183" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 你可以双击应用程序日志中对应的记录并检查其中的内容：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204242226025993.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-24 21-40-30" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204242226134706.png" alt="2012-4-24 21-40-30" width="595" height="418" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 上面的代码在监听器集合中添加了一个新的监听器，所以你将同时在调试输出窗口和Windows 事件日志中看到消息。如果你想移除默认的监听器而只使用事件日志的话，你需要使用RemoveAt() 方法，如下所示：&lt;/p&gt;&lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: green;"&gt;//Create a trace listener for the event log.&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;EventLogTraceListener &lt;/span&gt;eltl = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;EventLogTraceListener&lt;/span&gt;(&lt;span style="color: #a31515;"&gt;"TraceLog"&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green;"&gt;//Remove the default listener&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Listeners.RemoveAt(0);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green;"&gt;//Write output to the event log.&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Entered in Main()"&lt;/span&gt;);&lt;br/&gt;}&lt;p&gt;&lt;strong&gt;TextWriterTraceListener 类&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;我们将要通过检查TextWriterTraceListener类来进一步了解监听器。当你需要将跟踪消息直接写到一个文本文件中或者一个控制台应用程序中的话使用它会很有用。事实上，在创建一个TextWriterTraceListener对象过程中，你可以确定使用一个TextWriter 对象或者一个Stream 对象。使用一个Stream 对象允许你设定更多关于如何处理文件流的选项。下面的代码片段，TraceConsole.cs, 显示了如何在控制台中跟踪消息：&lt;/p&gt;&lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: green;"&gt;//Remove the default listener.&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Listeners.RemoveAt(0);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green;"&gt;//Add a console listener&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Listeners.Add(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;TextWriterTraceListener&lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.Out));&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green;"&gt;//Write a trace message.&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Entered in Main()"&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;}&lt;p&gt;由于在类的构造函数中设定Console.Out 流作为监听器，所以我们的控制台程序将显示跟踪消息：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204242226147596.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-24 21-51-43" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204242226154315.png" alt="2012-4-24 21-51-43" width="421" height="72" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;最后，让我们看看如何添加文本日志文件作为监听器。我们需要添加一个新的TextWriterTraceListener 对象，在构造函数中使用一个FileStream 对象。当程序结束以后，你需要使用Trace 类提供的静态Close() 方法来显式地关闭日志同时写入所有消息。在下面的代码中，Debugging.s, 使用主次线程来跟踪消息：&lt;/p&gt;&lt;span style="color: blue;"&gt;static void &lt;/span&gt;WritingThread()&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: green;"&gt;//Trace an info message&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;" - Entered WritingThread()"&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;.Sleep(1000);&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: green;"&gt;//Trace an info message&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;" - Slept for 1 second..."&lt;/span&gt;);&lt;br/&gt;}&lt;p&gt;线程使用WritingThread() 方法来睡眠一秒然后写一些跟踪消息。&lt;/p&gt;&lt;p&gt;这里我们创建了一个新的FileStream 对象，如果文件存在则打开否则新建一个。然后，我们在Add()方法中创建一个TextWriterTraceListener 类的新的实例并将其添加到监听器集合中：&lt;/p&gt;&lt;span style="color: #2b91af;"&gt;FileStream &lt;/span&gt;fs = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;FileStream&lt;/span&gt;(&lt;span style="color: #a31515;"&gt;@"C:\Debugging.log"&lt;/span&gt;, &lt;span style="color: #2b91af;"&gt;FileMode&lt;/span&gt;.OpenOrCreate);&lt;br/&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Listeners.Add(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;TextWriterTraceListener&lt;/span&gt;(fs));&lt;p&gt;启动线程以后，代码等待用户输入回车键然后关闭监听器程序并将所有跟踪信息写入到日志文件中：&lt;/p&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Entered Main()"&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;&lt;span style="color: #2b91af;"&gt;Thread &lt;/span&gt;t = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadStart&lt;/span&gt;(WritingThread));&lt;br/&gt;t.Start();&lt;br/&gt;&lt;br/&gt;&lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.ReadLine();&lt;br/&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Close();&lt;p&gt;代码的输出结果如下：&lt;/p&gt;&lt;p&gt;2012/4/24 22:22:35- Entered Main() &lt;br /&gt;2012/4/24 22:22:35- Entered WritingThread()  &lt;br /&gt;2012/4/24 22:22:36- Slept for 1 second...&lt;/p&gt;&lt;p&gt;Trace 类提供了一个有用的属性IndentLevel 来缩进跟踪消息。例如，你可以在主次线程中使用不同的缩进级别写跟踪消息。在上面代码中添加如下一行代码，我们就可以轻松地实现这个功能：&lt;/p&gt;&lt;span style="color: blue;"&gt;private static void &lt;/span&gt;WritingThread()&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: green;"&gt;//Setting indent level&lt;br/&gt;    &lt;/span&gt;&lt;strong&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.IndentLevel = 2;&lt;/strong&gt;&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Entered WritingThread()"&lt;/span&gt;);&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;.Sleep(1000);&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Slept for 1 second..."&lt;/span&gt;);&lt;br/&gt;}&lt;p&gt;修改后的代码输出结果如下：&lt;/p&gt;&lt;p&gt;2012/4/24 22:22:35- Entered Main() &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2012/4/24 22:22:35- Entered WritingThread()  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2012/4/24 22:22:36- Slept for 1 second...&lt;/p&gt;&lt;p&gt;你也可以使用Indent() 和 Unindent() 方法来对应的增加或者减少缩进等级。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;下一篇介绍跟踪开关&amp;hellip;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/danielWise/aggbug/2468989.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/danielWise/archive/2012/04/24/2468989.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/danielWise/archive/2012/04/23/2467143.html</id><title type="text">C# 线程手册 第六章 线程调试与跟踪 代码跟踪</title><summary type="text">我们下一个将要分析的代码检测技术是跟踪。在一个多线程应用程序中，这个技术非常重要。当已经启动了多个任务时，你可以跟踪一个线程的行为和相互之间的各个线程之间的影响。我们稍后将看到在这种情况下使用调试器是不现实的。.NET Framework 提供了很多有用的类来帮助开发人员轻松地实现跟踪功能。让我们看一下.NET Framework 提供的System.Diagnostics 命名空间中的跟踪类。 1. Trace: 这个类有很多向一个监听器写消息的静态方法。默认情况下，VS.NET 中的调试输出窗口将被用来作为监听程序，由于使用了监听器集合，所以你可以添加不同的监听器，比如文本监听器或者Wi.</summary><published>2012-04-23T13:55:00Z</published><updated>2012-04-23T13:55:00Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><link rel="alternate" href="http://www.cnblogs.com/danielWise/archive/2012/04/23/2467143.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/archive/2012/04/23/2467143.html"/><content type="html">&lt;p&gt;&amp;nbsp; 我们下一个将要分析的代码检测技术是跟踪。在一个多线程应用程序中，这个技术非常重要。当已经启动了多个任务时，你可以跟踪一个线程的行为和相互之间的各个线程之间的影响。我们稍后将看到在这种情况下使用调试器是不现实的。.NET Framework 提供了很多有用的类来帮助开发人员轻松地实现跟踪功能。让我们看一下.NET Framework 提供的System.Diagnostics 命名空间中的跟踪类。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 1. Trace: 这个类有很多向一个监听器写消息的静态方法。默认情况下，VS.NET 中的调试输出窗口将被用来作为监听程序，由于使用了监听器集合，所以你可以添加不同的监听器，比如文本监听器或者Windows事件日志监听器。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 2. Debug: 这个类的方法和Trace 类的一样，都向一个监听器应用程序中写入信息。这两个类在使用上最大的不同是，Trace 用于运行阶段，Debug 用于开发阶段。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 3. BooleanSwitch: 这个类允许我们定义一个开关来打开/关闭跟踪消息。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 4. TraceSwitch: 这个类提供四个不同的跟踪级别来帮助开发人员选择发送不同级别的消息给监听器。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Trace 类&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在这部分，我们将分析Trace 类中使用最频繁的几个方法。这些方法由.NET Framework包装好后提供给我们。Trace 类位于System.Diagnostics 命名空间中并提供很多静态方法来发送消息给监听程序。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 下表列出来由Trace 类提供的一些静态方法：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204232154518295.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-23 21-04-07" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204232154522048.png" alt="2012-4-23 21-04-07" width="546" height="306" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 这些方法的默认行为取决于选择的监听器程序。例如，当使用默认监听器的时候，Assert() 方法会显示一个消息框。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;默认监听器程序&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; Trace 类提供一个允许我们添加一个新的监听器程序的监听器集合。当没有向监听器集合中添加新的监听器对象时，Trace 类就会使用默认的监听器程序：调试输出窗体。这个窗体由Visual Studio.NET IDE 在调试过程中提供。让我们来看一个简单的例子，TraceExample:&lt;/p&gt;&lt;span style="color: green;"&gt;/*************************************&lt;br/&gt;/* Copyright (c) 2012 Daniel Dong&lt;br/&gt; * &lt;br/&gt; * Author：Daniel Dong&lt;br/&gt; * Blog：  www.cnblogs.com/danielWise&lt;br/&gt; * Email： guofoo@163.com&lt;br/&gt; * &lt;br/&gt; */&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Linq;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Text;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Diagnostics;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;namespace &lt;/span&gt;TraceExample&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Program&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Enter Main()"&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: blue;"&gt;for &lt;/span&gt;(&lt;span style="color: blue;"&gt;int &lt;/span&gt;i = 0; i &amp;lt; 6; i++)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(i);&lt;br/&gt;            }&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Exiting from Main()"&lt;/span&gt;);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 这段代码真的非常简单；当进入和退出Main() 方法时它打出调试信息，在循环中累加变量值。在下一个截图中，你可以看到Visual Studio.NET 输出监听器是如何显示信息的：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204232154532704.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-23 21-26-57" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204232154556773.png" alt="2012-4-23 21-26-57" width="579" height="241" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; Trace 类也提供了两个有用的方法来断言错误提示：Assert() 和 Fail(). 前者允许开发人员检查作为参数传入的条件是否满足并在条件不满足时向监听器中写入一条消息。后者会在每次发生失败时向监听器中写入一条消息。当监听器集合中没有其他监听器对象时，Assert() 方法显示一个消息对话框来提供用户断言失败。下面的代码片段，TraceAssert.cs, 可以在SQL Server 服务被故意停止且引发一个连接错误的时候测试出来：&lt;/p&gt;&lt;span style="color: green;"&gt;/*************************************&lt;br/&gt;/* Copyright (c) 2012 Daniel Dong&lt;br/&gt; * &lt;br/&gt; * Author：Daniel Dong&lt;br/&gt; * Blog：  www.cnblogs.com/danielWise&lt;br/&gt; * Email： guofoo@163.com&lt;br/&gt; * &lt;br/&gt; */&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Linq;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Text;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Diagnostics;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Threading;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Data.SqlClient;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;namespace &lt;/span&gt;TraceExample&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Program&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        [&lt;span style="color: #2b91af;"&gt;STAThread&lt;/span&gt;]&lt;br/&gt;        &lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: green;"&gt;//Create a thread&lt;br/&gt;            &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Thread &lt;/span&gt;t = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadStart&lt;/span&gt;(DBThread));&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: green;"&gt;//Start the thread&lt;br/&gt;            &lt;/span&gt;t.Start();&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;private static void &lt;/span&gt;DBThread()&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: green;"&gt;//Create a connection object&lt;br/&gt;            &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;SqlConnection &lt;/span&gt;dbConn = &lt;br/&gt;                &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;SqlConnection&lt;/span&gt;(&lt;span style="color: #a31515;"&gt;@"Data Source=DANIELFORWARD\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True"&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: green;"&gt;//Create a command object to execute a SQL statement&lt;br/&gt;            &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;SqlCommand &lt;/span&gt;dbComm = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;SqlCommand&lt;/span&gt;(&lt;span style="color: #a31515;"&gt;"SELECT * FROM Region"&lt;/span&gt;, dbConn);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;SqlDataReader &lt;/span&gt;dr = &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(&lt;span style="color: #2b91af;"&gt;DateTime&lt;/span&gt;.Now + &lt;span style="color: #a31515;"&gt;"- Execute SQL statement"&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: blue;"&gt;try&lt;br/&gt;            &lt;/span&gt;{&lt;br/&gt;                &lt;span style="color: green;"&gt;//Open the connection to the database&lt;br/&gt;                &lt;/span&gt;dbConn.Open();&lt;br/&gt;&lt;br/&gt;                &lt;span style="color: green;"&gt;//Assert that the connection opened&lt;br/&gt;                &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Assert(dbConn.State == System.Data.&lt;span style="color: #2b91af;"&gt;ConnectionState&lt;/span&gt;.Open, &lt;span style="color: #a31515;"&gt;"Error"&lt;/span&gt;, &lt;span style="color: #a31515;"&gt;"Connection failed..."&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;                &lt;span style="color: green;"&gt;//Execute the SQL statement&lt;br/&gt;                &lt;/span&gt;dr = dbComm.ExecuteReader(System.Data.&lt;span style="color: #2b91af;"&gt;CommandBehavior&lt;/span&gt;.CloseConnection);&lt;br/&gt;&lt;br/&gt;                &lt;span style="color: green;"&gt;//Assert that the statement executed OK&lt;br/&gt;                &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Assert(dr != &lt;span style="color: blue;"&gt;null&lt;/span&gt;, &lt;span style="color: #a31515;"&gt;"Error"&lt;/span&gt;, &lt;span style="color: #a31515;"&gt;"The SqlDataReader is null."&lt;/span&gt;);&lt;br/&gt;&lt;br/&gt;                &lt;span style="color: blue;"&gt;while &lt;/span&gt;(dr.Read())&lt;br/&gt;                {&lt;br/&gt;                    &lt;span style="color: green;"&gt;//Reading records&lt;br/&gt;                    &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.WriteLine(dr[0].ToString());&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;            &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: green;"&gt;//Log the error to the Trace application&lt;br/&gt;                &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Fail(&lt;span style="color: #a31515;"&gt;"An error occured in database access, details: " &lt;/span&gt;+ ex.Message);&lt;br/&gt;            }&lt;br/&gt;            &lt;span style="color: blue;"&gt;finally&lt;br/&gt;            &lt;/span&gt;{&lt;br/&gt;                &lt;span style="color: blue;"&gt;if &lt;/span&gt;(!dr.IsClosed &amp;amp;&amp;amp; dr != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;br/&gt;                {&lt;br/&gt;                    dr.Close();&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 在Main() 方法中，创建并启动了一个新线程。新线程运行DBThread()中的代码。代码简单地连接了下SQL Server 的Northwind数据库，从Region表中收集所有数据。如果SQL Server 不可用，在执行代码过程中会弹出下面的断言失败窗体。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204232154564953.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-23 21-53-30" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204232154589611.png" alt="2012-4-23 21-53-30" width="470" height="370" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 引发异常断言的代码为：&lt;/p&gt;&lt;span style="color: #2b91af;"&gt;Trace&lt;/span&gt;.Assert(dbConn.State == System.Data.&lt;span style="color: #2b91af;"&gt;ConnectionState&lt;/span&gt;.Open, &lt;span style="color: #a31515;"&gt;"Error"&lt;/span&gt;, &lt;span style="color: #a31515;"&gt;"Connection failed..."&lt;/span&gt;);&lt;p&gt;&amp;nbsp; 你可以看到，第一个参数检查连接状态是否打开。当连接没有打开时它将被设置为false, 然后会显示断言失败。你将会从本章的后续部分了解到也可以使用配置文件禁用消息跟踪。那样的话，你就可以在运行时决定是否显示断言消息。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;下一篇介绍如何使用不同的监听程序&amp;hellip;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/danielWise/aggbug/2467143.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/danielWise/archive/2012/04/23/2467143.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/danielWise/archive/2012/04/21/2461655.html</id><title type="text">C# 线程手册 第六章 线程调试与跟踪</title><summary type="text">调试与跟踪是两个经常使用到的技术，这两种技术对程序员是非常必要的。前者允许开发人员分析一个程序的变量值以及代码，一步一步跟踪代码流程。后者允许我们跟踪我们应用程序的行为，在一个监听器(一个日志文件，Windows 事件日志等)中显示信息。它们是创建鲁棒性应用程序的基础，因为它们给开发人员提供了一个简单的方式去跟踪并理解我们的应用程序是如何工作的。这两种技术最大的不同是跟踪可以在一个程序运行过程中完成，而调试用于设计阶段以及我们的最终版程序发布之前。 桌面应用程序开发人员通常有出色的调试技术支持，能够使用断点去检查变量内容。.NET 在这方面也不例外，但是在一个多线程应用程序中使用断点来观察一.</summary><published>2012-04-21T08:41:00Z</published><updated>2012-04-21T08:41:00Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><link rel="alternate" href="http://www.cnblogs.com/danielWise/archive/2012/04/21/2461655.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/archive/2012/04/21/2461655.html"/><content type="html">&lt;p&gt;&amp;nbsp; 调试与跟踪是两个经常使用到的技术，这两种技术对程序员是非常必要的。前者允许开发人员分析一个程序的变量值以及代码，一步一步跟踪代码流程。后者允许我们跟踪我们应用程序的行为，在一个监听器(一个日志文件，Windows 事件日志等)中显示信息。它们是创建鲁棒性应用程序的基础，因为它们给开发人员提供了一个简单的方式去跟踪并理解我们的应用程序是如何工作的。这两种技术最大的不同是跟踪可以在一个程序运行过程中完成，而调试用于设计阶段以及我们的最终版程序发布之前。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 桌面应用程序开发人员通常有出色的调试技术支持，能够使用断点去检查变量内容。.NET 在这方面也不例外，但是在一个多线程应用程序中使用断点来观察一些情况是一个固有的问题，在这章我们将关注这个问题。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 除了桌面应用程序，开发人员一直以来都苦于没有一个好的调试器来跟踪诸如ASP.NET 等Web 应用程序。为了获知一个变量的值或者代码流程(这些通常由调试器处理)，ASP 开发人员不得不使用Response.Write() 语句来跟踪代码，显示一些诸如进入函数，退出循环之类的消息。然后在他们测试完ASP应用程序之后，他们还需要把这些不需要的语句去掉。这不是调试一个程序的最好的方式。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 幸运的是，NET 为下一代的ASP 开发人员带来了调试功能并提供了四个有用的类：Trace, Debug, BooleanSwitch 以及 TraceSwitch. 除此之外，任何.NET 语言都可以使用这些类，所以每个选择使用Visual Studio .NET 来创建应用程序的开发人员都可以使用Visual Studio.NET 提供的工具调试代码。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 多种跟踪与调试技术对使用线程的应用程序来说非常重要。如果功能实现的很好的话，这些技术允许开发人员跟踪每个线程的行为，发现任何应用程序异常，比如一个未知的资源消耗、抢夺bugs 等等。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在这章，我们将按照以下顺序同时分析跟踪和调试技术:&lt;/p&gt;&lt;p&gt;&amp;nbsp; 1. 使用Visual Studio .NET 调试分析器以及它的强大工具；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 2. 使用.NET 跟踪类来在我们的代码中实现这个特性；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 3. 创建一个能够使用所有这些跟踪技术/工具的程序。&lt;/p&gt;&lt;p&gt;在这章，Visual Studio.NET 对于使用尽可能多的跟踪和调试技术来说是很必要的，通过/d:TRACE=TRUE 开关，一些跟踪功能可以使用命名行实现。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;创建应用程序&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 通常，当你创建一个程序(或者程序的一部分)时，你首先实现代码部分然后尝试运行程序。有时候程序按照你期望的那样运行；但是通常来说并不是这样。当程序不按照我们期望的运行时，你通过仔细地检查你写的代码来尝试发现究竟发生了什么。在Visual Studio..NET 中，你可以使用调试器添加几个断点并让程序执行停在邪恶(可能出错的，以下类同)方法附近或者之前，然后一步步调试到邪恶方法内部，检查变量值并精确地了解哪里出错了。最后，当所有功能都正常以后你可以创建一个发布版本(一个没有任何调试工具使用的变量的版本)，并把它分发出去。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在这种类型的应用程序中，在开发过程中，你可以插入跟踪代码。事实上，即便程序目前工作地非常好，总会有一条case (尤其是当一些外部的、第三方的组件失败时)还没有被发现。在这种情况下，如果你在代码里加了跟踪指令，你可以打开跟踪并检查结果日志来了解可能发生了什么事情。此外，跟踪功能是一个发现程序执行任务时哪里消耗资源多或者哪里花费太长时间的利器。在使用线程的应用程序中，你应该使用跟踪功能，否则的话观察每个线程的行为、确定跟踪条件、发现潜在的死锁条件或者时间消耗问题会变得很困难。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 跟踪，调试以及性能检测技术通常被称作&lt;strong&gt;仪器仪表&lt;/strong&gt;。这是指能够监测一个程序的性能、行为并能诊断错误和问题的能力。所以，一个支持仪器仪表功能的程序应该包括：&lt;/p&gt;&lt;p&gt;&amp;nbsp; 1. 调试：在开发过程中解决错误和问题；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 2. 代码跟踪：在程序执行过程中在一个监听程序中接收信息；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 3. 性能计数器：监控一个程序性能&lt;/p&gt;&lt;p&gt;让我们通过在我们的程序中添加一些指令来检查.NET Framework 给我们提供了什么。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;调试代码&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 通常，当你测试你的程序来查看它的行为是否为你所期待的那样时，你开始仔细地检查你所写的代码。如果你使用Visual Studio.NET 类创建你的程序，它会提供很多出色的工具来调试你的程序。此外，无论你使用什么语言开发程序，你都将使用同样的调试器和工具。更狠的是，基础调试器功能由Visual Basic 6 和 Visual C++ IDEs 继承下来的，这意味着大多数开发人员都应该比较熟悉。然而，我们将不会花费太多时间在调试器上，我们主要关注与线程相关的功能。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 新的调试器提供以下功能：&lt;/p&gt;&lt;p&gt;&amp;nbsp; 1. 使用同样的工具调试由不同语言写的不同应用程序，并支持由混合语言(C# 中嵌入C++)写成的应用程序；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 2. &lt;strong&gt;调试SQL Server 存储过程&lt;/strong&gt;；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 3. 调试.NET Framework 和Win32 原生代码，假设你在调试Visual C#.NET 应用程序而你的线程使用了一个COM+组件，你可以使用同样的调试器同时调试这两个程序；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 4. 一个更加强大的远程调试器&lt;/p&gt;&lt;p&gt;&amp;nbsp; 如果你曾经使用过Visual Basic 6 调试器，你将知道它之中的一些功能在新调试器中已经被删除了。比如在调试过程中改变代码然后继续执行的功能。如果使用Visual Studio.NET 调试器，那么你会发现这个功能已经不存在了，因为对代码的每次修改都要求重新编译一次(&lt;span style="color: #ff0000;"&gt;x64 程序不允许修改，x86 程序是允许的&lt;/span&gt;)。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在本书的这部分，我们将分析由Visual Studio.NET IDE 提供的调试工具，它对多线程应用程序开发过程的测试和发现错误是非常有用的。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Visual Studio.NET 调试器&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 使用Visual Studio .NET 调试器可以简单地在你希望检查的代码位置插入一个断点来中断程序的执行。当你的程序被阻塞以后，调试器提供了很多工具来检查和编辑变量内容，检查内存和调用堆栈等等。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;设置调试器参数&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 为了使用Visual Studio.NET 调试器，你不得不使用Debug 模式编译程序。在那种方式，除了实现代码你还可以添加一些额外的调试信息。当所有工作看起来很正常时，你将在重编译代码后发布程序，选择Release 模式，这种模式会移除所有的调试信息。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 当一个新的调试过程开始后，很多资源被加载如内存。事实上，调试器把变量代码填充到内存中以允许我们调试非托管代码，SQL Server 存储过程等等。当你不需要调试非托管代码时将这些特性去除是一个很好的主意。你可以通过Solution Explorer-&amp;gt;选中工程-&amp;gt;右键选择属性-&amp;gt;属性页对话框 来修改调试器设置。对一个Windows 应用程序来说，将会出现下面的对话框：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640427397.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-4-21 16-23-43" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640434954.png" alt="2012-4-21 16-23-43" width="493" height="210" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在编译一个Debug 工程时，输出目将包含exe/dll 和一个pdb(程序数据库) 文件。因为IL 在数组中存储参数值和私有成员值，所以这些变量的原始名字都不存在了 - 被替换成一些与调试相关的其他信息。当编译一个工程来调试时，或者在命令行编译中使用/debug:full 参数，都会在编译过程中生成一个pdb 文件。exe/dll 文件包含一个指向pdb 文件的绝对路径，如果调试器找不到程序数据库文件，它就开始在程序的当前路径以及属性对话框配置页设置的路径中寻找。如果最后调试器找不到一个pdf 文件，那么它会重新生成一个。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;调试器窗口&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 一旦你已经在Visual Studio.NET 中加载你的工程，你就可以通过运行它来对其调试，等到代码运行到断点处，然后使用F10/F11 键来跳过/进入方法调用。如果你不是使用发布版本，你将看到IDE显示很多窗口。在调试期间这些窗口将填充很多变量值，对象列表，调用堆栈等等。让我们更加仔细地检查这些调试工具，并看看如何使用它们来帮助调试多线程应用程序。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;本地窗口&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 这个窗口允许你检查并修改你正在调试的方法中定义的本地变量的值(包括方法参数的值)。例如，调试上一篇博客中ThreadPoolManager的Main() 方法，你将看到两个变量的内容：tp 和 p, 如下截图所示：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640433351.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-4-21 16-25-53" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640447529.png" alt="2012-4-21 16-25-53" width="452" height="131" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 你可以通过Debug-&amp;gt;Window-&amp;gt;Local 菜单激活这个窗口，你也可以通过Ctrl + Alt +V 激活这个窗口。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;查看窗口&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 你可以从源代码中抓取变量并把它们扔到Watch 窗口来监测它们的值和结构。在下面的截图中，上图中的tp对象被放入了查看窗口中。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/20120421164044626.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-4-21 16-27-57" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640455676.png" alt="2012-4-21 16-27-57" width="450" height="175" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 你可以点击+号来展开树节点，然后检查并改变属性值。你可以通过Ctrl + Alt + W 激活窗口。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 你可以从源代码中选择一个变量然后右键选择查看来监测变量值。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;常用窗口 - 立即窗口&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 这个窗口提供一个文本输入框，你可以查询一个变量内容并改变变量值。当你需要收集变量内容时你需要使用在表达式之前加一个问好。在下面的截图中，我们检查了tp对象的Debug属性，把它设置为false, 然后显示它的值。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640452121.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-4-21 16-30-48" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640451074.png" alt="2012-4-21 16-30-48" width="358" height="137" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 除了以上功能，这个窗口还允许你使用IDE命令，比如创建一个文件或者工程，寻找一个字符串或者任何你可以通过Visual Studio.NET 实现的操作。你可以使用&amp;gt;cmd 命令将立即窗口切换成命令模式。在命令模式中，你将得到IDE 的职能感知功能来获得需要的命令。你可以通过&amp;gt;immed 命令切换回来。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;单步调试&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 我们已经简单介绍了调试窗口的几个有用工具，现在可以专注代码方面。Visual Studio.NET 调试器允许开发人员在一行和多行代码间单步调试，在运行时查看程序行为。此外，你可以调试非托管代码和SQL Server 存储过程。调试器提供了三种不同的方式来单步调试代码：&lt;/p&gt;&lt;p&gt;&amp;nbsp; 1. Step Into:&amp;nbsp; 按F11键你将一次执行一步代码，进入调试过程中遇到的每个函数的函数体(仅当源代码和调试参数存在时)；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 2. Step Over: 按F10键你将一次执行一个函数而不会进入函数体内部(按照一行代码来执行一个函数)；&lt;/p&gt;&lt;p&gt;&amp;nbsp; 3. Step Out: 按Shift + F11 键你将执行当前方法中所有的剩余代码，并到达调用这个函数的方法的下一行&lt;/p&gt;&lt;p&gt;&amp;nbsp; 每次你通过这些按键单步调试到下一行时，你都在执行当前高亮的代码。&lt;/p&gt;&lt;p&gt;&amp;nbsp; Visual Studio.NET 调试器提供的另外一个有用的功能是运行到光标。你可以通过这个功能执行高亮代码行和光标所在代码行之间的所有代码。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 最后，Visual Studio.NET 调试器提供了一个方式来改变我们程序的执行点。你可以通过运行调试器并在菜单中设置下一条语句来决定移动程序的执行点。要小心使用这个特性，因为原来位置和新位置之前的每行代码都可能出错。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;设置断点&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在大的源代码应用程序中，在到达你感兴趣的方法之前单步调试之前的所有代码是不现实的。调试器提供在代码中设置断点的功能。功能如其名，一个断点是你的程序必须停止的点。你可以在开始调试之前设置断点，简单地把光标放置在需要设定断点的代码位置并按F9键 - 或者在左边界面点击鼠标左键。一个红色高亮标志将会在你想要代码停止的地方显示出来，同时会在代码主窗体的左边栏添加一个红色圆点。你可以通过去掉红色圆点或者再次点击F9键来去除断点。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 你可以在断点窗口管理设置的所有断点。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640469155.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-4-21 16-32-52" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640461697.png" alt="2012-4-21 16-32-52" width="461" height="94" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 使用这个窗口，你可以添加一个新断点，删除一个或者所有断点，禁用所有断点，添加并移除窗口指定列并查看断点属性。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 通过断点，你可以中断一个线程的执行并检查它当前的上下文内容。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 从管理断点窗口中选择断点属性，将会显示一个新的对话框，在这个对话框中你可以设置仅当一个特定变量改变它的内容时才激活一个断点。你需要在断点属性页中确定变量的名字。这个功能在多线程场景中很有用，因为当一些不可预期的错误发生时你可以察觉。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 最后，管理断点窗口中的Hit 数量对话框允许开发人员仅当一个断点到达特定触发数量时才开启。这个功能在多线程应用程序中也很有用，因为它能帮助你查看多久生成一个线程。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640473366.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-4-21 16-35-18" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640475384.png" alt="2012-4-21 16-35-18" width="463" height="222" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 从下拉列表中，你可以选择与断点关联的条件。例如，你可以在将要退出循环之前激活断点。你可以选择那些触发次数等于设置值的断点。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 为了在到达断点之前执行所有代码你需要使用F5键。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;调试线程&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; Visual Studio.NET 调试器提供了一个特别的窗口来在调试期间管理线程。你可以通过Debug-&amp;gt;Windows-&amp;gt;Thread 或者Ctrl + Alt + H 来显示这个窗口。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640477925.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-4-21 16-36-09" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204211640488547.png" alt="2012-4-21 16-36-09" width="683" height="188" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 线程窗口包含以下列：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/2012042116432483.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="2012-4-21 16-42-27" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/20120421164324149.png" alt="2012-4-21 16-42-27" width="460" height="173" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 你可以在线程窗口中通过简单地双击线程组件来在不同线程间切换。此外，在一个线程上右键，你可以选择冻结菜单来暂停线程执行。你可以选择Thaw 菜单来恢复冻结的线程状态。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;下一篇介绍代码跟踪技术&amp;hellip;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/danielWise/aggbug/2461655.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/danielWise/archive/2012/04/21/2461655.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/danielWise/archive/2012/04/19/2458520.html</id><title type="text">C# 线程手册 第五章 多线程应用程序 .NET 中的扩展性</title><summary type="text">如果你有一个多处理器系统，那么你将有机会体验到线程真正的价值。Windows 操作系统负责向处理器分配线程，正如你在本书中看到的那样，启动一个进程会自动运行一个线程。.NET Framework 不提供细粒度的处理器分配控制，而是允许操作系统控制调度，这是由于操作系统比CLR 更加了解处理器的负载。CLR 负责提供一些诸如整个进程运行在哪个处理器上的控制。然而，一个进程中的所有线程都会运行在一个处理器上，控制进程中的线程运行在哪个处理器上的内容不会在本书中介绍。 如果你只有一个主线程，那么在这个线程中的每个任务都会运行在同一个处理器上。然而，如果系统中又新建了一个线程，那么操作系统负责调度这.</summary><published>2012-04-19T14:27:00Z</published><updated>2012-04-19T14:27:00Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><link rel="alternate" href="http://www.cnblogs.com/danielWise/archive/2012/04/19/2458520.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/archive/2012/04/19/2458520.html"/><content type="html">&lt;p&gt;&amp;nbsp; 如果你有一个多处理器系统，那么你将有机会体验到线程真正的价值。Windows 操作系统负责向处理器分配线程，正如你在本书中看到的那样，启动一个进程会自动运行一个线程。.NET Framework 不提供细粒度的处理器分配控制，而是允许操作系统控制调度，这是由于操作系统比CLR 更加了解处理器的负载。CLR 负责提供一些诸如整个进程运行在哪个处理器上的控制。然而，一个进程中的所有线程都会运行在一个处理器上，控制进程中的线程运行在哪个处理器上的内容不会在本书中介绍。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 如果你只有一个主线程，那么在这个线程中的每个任务都会运行在同一个处理器上。然而，如果系统中又新建了一个线程，那么操作系统负责调度这个新生成的线程运行在哪个处理器上。决定在哪个处理器上运行线程也是要消耗一些处理器资源的，对小任务来说，这种决定的消耗通常来说是不值得的，因为这个任务的运行时间可能和操作系统将任务调度到处理器上的时间差不多。然而，这种调度消耗的时间在Windows 各版本的持续改进中已经越来越短了，对那些不是特别琐碎的任务来说，使用线程会让你的任务获得性能方面的提升。在对称多处理器系统(SMP)中使用线程的优势非常明显，因为处理器可以高效的处理应用程序的负载均衡。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在下一部分，我们将要描述如何创建一个管理线程的线程池管理器，并可以保证线程池中的最小和最大线程数量和重用空闲线程。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;一个线程池管理器&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 纵观本书，你已经看到创建线程的几种方式，同时在这一章我们已经描述了ThreadPool 类为生命周期短的线程使用操作系统自己的线程池。我们可以创建一个为任何应用程序的请求提供一个特定数量线程的类。这将使线程更容易地被你自己的代码所管理，由于你可以使用一个之前已经实例化的线程对象所以线程执行起来会更快。这个类将把到目前为止我们学到的所有知识串联起来，你将可以在自己的多线程应用程序中使用它。我们将一步步的解释这个类，最后会提供一个应用程序来测试这个类是否按照我们期望的那样执行。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 好吧，我们现在就开始实现我们的线程池类:&lt;/p&gt;&lt;span style="color: green;"&gt;/*************************************&lt;br/&gt;/* Copyright (c) 2012 Daniel Dong&lt;br/&gt; * &lt;br/&gt; * Author：Daniel Dong&lt;br/&gt; * Blog：  www.cnblogs.com/danielWise&lt;br/&gt; * Email： guofoo@163.com&lt;br/&gt; * &lt;br/&gt; */&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Linq;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Text;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Threading;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;namespace &lt;/span&gt;GenThreadPool&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;IThreadPool&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;void &lt;/span&gt;AddJob(&lt;span style="color: #2b91af;"&gt;Thread &lt;/span&gt;jobToRun);&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Stats &lt;/span&gt;GetStats();&lt;br/&gt;    }&lt;br/&gt;}&lt;span style="color: blue;"&gt;public void &lt;/span&gt;AddJob(&lt;span style="color: #2b91af;"&gt;Thread &lt;/span&gt;jobToRun)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;if &lt;/span&gt;(jobToRun != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: blue;"&gt;lock &lt;/span&gt;(&lt;span style="color: blue;"&gt;this&lt;/span&gt;)&lt;br/&gt;        {&lt;br/&gt;            PendingJobs.Add(jobToRun);&lt;br/&gt;            &lt;span style="color: green;"&gt;//int index = FindFirstIdleThread();&lt;br/&gt;            &lt;/span&gt;&lt;span style="color: blue;"&gt;int &lt;/span&gt;index = 0;&lt;br/&gt;            &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mDebug)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"First idle thread is " &lt;/span&gt;+ index);&lt;br/&gt;            }&lt;br/&gt;            &lt;span style="color: blue;"&gt;if &lt;/span&gt;(index == -1)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: blue;"&gt;if &lt;/span&gt;((mMaxThreads == -1) || (mAvailableThreads.Count &amp;lt; mMaxThreads))&lt;br/&gt;                {&lt;br/&gt;                    &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mDebug)&lt;br/&gt;                    {&lt;br/&gt;                        &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Creating a new thread"&lt;/span&gt;);&lt;br/&gt;                    }&lt;br/&gt;                    &lt;span style="color: #2b91af;"&gt;Thread &lt;/span&gt;t = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadStart&lt;/span&gt;(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;GenPool&lt;/span&gt;(&lt;span style="color: blue;"&gt;this&lt;/span&gt;, &lt;span style="color: blue;"&gt;this&lt;/span&gt;).Run));&lt;br/&gt;                    &lt;span style="color: #2b91af;"&gt;ThreadElement &lt;/span&gt;e = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadElement&lt;/span&gt;(t);&lt;br/&gt;                    e.Idle = &lt;span style="color: blue;"&gt;false&lt;/span&gt;;&lt;br/&gt;                    e.GetMyThread().Start();&lt;br/&gt;&lt;br/&gt;                    &lt;span style="color: blue;"&gt;try&lt;br/&gt;                    &lt;/span&gt;{&lt;br/&gt;                        mAvailableThreads.Add(e);&lt;br/&gt;                    }&lt;br/&gt;                    &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;OutOfMemoryException&lt;/span&gt;)&lt;br/&gt;                    {&lt;br/&gt;                        &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Out of Memory"&lt;/span&gt;);&lt;br/&gt;                        mAvailableThreads.Add(e);&lt;br/&gt;                        &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Added Job again"&lt;/span&gt;);&lt;br/&gt;                    }&lt;br/&gt;                    &lt;span style="color: blue;"&gt;return&lt;/span&gt;;&lt;br/&gt;                }&lt;br/&gt;                &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mDebug)&lt;br/&gt;                {&lt;br/&gt;                    &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"No Threads Available..." &lt;/span&gt;+ &lt;span style="color: blue;"&gt;this&lt;/span&gt;.GetStats().ToString());&lt;br/&gt;                }&lt;br/&gt;                &lt;span style="color: blue;"&gt;else&lt;br/&gt;                &lt;/span&gt;{&lt;br/&gt;                    &lt;span style="color: blue;"&gt;try&lt;br/&gt;                    &lt;/span&gt;{&lt;br/&gt;                        &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mDebug)&lt;br/&gt;                        {&lt;br/&gt;                            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Using an existing thread..."&lt;/span&gt;);&lt;br/&gt;                        }&lt;br/&gt;                        ((&lt;span style="color: #2b91af;"&gt;ThreadElement&lt;/span&gt;)mAvailableThreads[index]).Idle = &lt;span style="color: blue;"&gt;false&lt;/span&gt;;&lt;br/&gt;                        &lt;span style="color: blue;"&gt;lock &lt;/span&gt;(((&lt;span style="color: #2b91af;"&gt;ThreadElement&lt;/span&gt;)mAvailableThreads[index]).GetMyThread())&lt;br/&gt;                        {&lt;br/&gt;                            &lt;span style="color: #2b91af;"&gt;Monitor&lt;/span&gt;.Pulse(((&lt;span style="color: #2b91af;"&gt;ThreadElement&lt;/span&gt;)mAvailableThreads[index]).GetMyThread());&lt;br/&gt;                        }&lt;br/&gt;                    }&lt;br/&gt;                    &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;                    {&lt;br/&gt;                        &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Error while resuing thread " &lt;/span&gt;+ ex.Message);&lt;br/&gt;                        &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mDebug)&lt;br/&gt;                        {&lt;br/&gt;                            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Value of index is " &lt;/span&gt;+ index);&lt;br/&gt;                            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Size of available threads is " &lt;/span&gt;+ mAvailableThreads.Count);&lt;br/&gt;                            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Availablee Threads is " &lt;/span&gt;+ mAvailableThreads.IsSynchronized);&lt;br/&gt;                        }&lt;br/&gt;                    }&lt;br/&gt;                }&lt;span style="color: green;"&gt;//end of else&lt;br/&gt;            &lt;/span&gt;}&lt;span style="color: green;"&gt;//lock&lt;br/&gt;        &lt;/span&gt;}&lt;span style="color: green;"&gt;//end of method&lt;br/&gt;    &lt;/span&gt;}&lt;br/&gt;}&lt;p&gt;上面的方法向线程池中添加一个任务。如果参数传递的任务不存在，那么就会退出这个方法。否则，它会在GenThreadPoolImpl实例上提供一个锁来保证没有其他线程尅添加或者删除一个任务。当任务被添加到列表中以后，列表中将存储有所有等待执行和已经完成的任务。FindFirstIdleThread() 方法返回可用线程列表中当前空闲的线程。如果返回值为-1则表示当前无可用线程，那么线程池就会尝试新建一个线程。&lt;/p&gt;&lt;span style="color: blue;"&gt;public void &lt;/span&gt;RemoveThread()&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;for &lt;/span&gt;(&lt;span style="color: blue;"&gt;int &lt;/span&gt;i = 0; i &amp;lt; mAvailableThreads.Count; i++)&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: blue;"&gt;if &lt;/span&gt;(((&lt;span style="color: #2b91af;"&gt;ThreadElement&lt;/span&gt;)mAvailableThreads[i]).GetMyThread().Equals(&lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;.CurrentThread))&lt;br/&gt;        {&lt;br/&gt;            mAvailableThreads.RemoveAt(i);&lt;br/&gt;            &lt;span style="color: blue;"&gt;return&lt;/span&gt;;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 上述方法会将当前线程从线程池中移除。当然，这个方法也可以用来将任务已经完成的线程和空闲时间超过MaxIdleTime所限定的时间线程移除。现在我们定义这个程序集的其他类：&lt;/p&gt;&lt;span style="color: green;"&gt;/*************************************&lt;br/&gt;/* Copyright (c) 2012 Daniel Dong&lt;br/&gt; * &lt;br/&gt; * Author：Daniel Dong&lt;br/&gt; * Blog：  www.cnblogs.com/danielWise&lt;br/&gt; * Email： guofoo@163.com&lt;br/&gt; * &lt;br/&gt; */&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Linq;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Text;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Threading;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;namespace &lt;/span&gt;GenThreadPool&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;GenPool&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;private object &lt;/span&gt;mLock;&lt;br/&gt;        &lt;span style="color: blue;"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;GenThreadPoolImpl &lt;/span&gt;mGn;&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;public &lt;/span&gt;GenPool(&lt;span style="color: blue;"&gt;object &lt;/span&gt;lockObj, &lt;span style="color: #2b91af;"&gt;GenThreadPoolImpl &lt;/span&gt;gn)&lt;br/&gt;        {&lt;br/&gt;            mLock = lockObj;&lt;br/&gt;            mGn = gn;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;public void &lt;/span&gt;Run()&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Thread &lt;/span&gt;job = &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;br/&gt;            &lt;span style="color: blue;"&gt;try&lt;br/&gt;            &lt;/span&gt;{&lt;br/&gt;                &lt;span style="color: blue;"&gt;while &lt;/span&gt;(&lt;span style="color: blue;"&gt;true&lt;/span&gt;)&lt;br/&gt;                {&lt;br/&gt;                    &lt;span style="color: blue;"&gt;while &lt;/span&gt;(&lt;span style="color: blue;"&gt;true&lt;/span&gt;)&lt;br/&gt;                    {&lt;br/&gt;                        &lt;span style="color: blue;"&gt;lock &lt;/span&gt;(mLock)&lt;br/&gt;                        {&lt;br/&gt;                            &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mGn.PendingJobs.Count == 0)&lt;br/&gt;                            {&lt;br/&gt;                                &lt;span style="color: blue;"&gt;int &lt;/span&gt;index = mGn.FindFirstIdleThreadCount();&lt;br/&gt;                                &lt;span style="color: blue;"&gt;if &lt;/span&gt;(index == -1)&lt;br/&gt;                                {&lt;br/&gt;                                    &lt;span style="color: blue;"&gt;return&lt;/span&gt;;&lt;br/&gt;                                }&lt;br/&gt;                                ((&lt;span style="color: #2b91af;"&gt;ThreadElement&lt;/span&gt;)mGn.AvailableThreads[index]).Idle = &lt;span style="color: blue;"&gt;true&lt;/span&gt;;&lt;br/&gt;                                &lt;span style="color: blue;"&gt;break&lt;/span&gt;;&lt;br/&gt;                            }&lt;br/&gt;                            job = (&lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;)mGn.PendingJobs[0];&lt;br/&gt;                            mGn.PendingJobs.RemoveAt(0);&lt;br/&gt;                        }&lt;span style="color: green;"&gt;//end of lock&lt;br/&gt;                        //run the job&lt;br/&gt;                        &lt;/span&gt;job.Start();&lt;br/&gt;                    }&lt;br/&gt;&lt;br/&gt;                    &lt;span style="color: blue;"&gt;try&lt;br/&gt;                    &lt;/span&gt;{&lt;br/&gt;                        &lt;span style="color: blue;"&gt;lock &lt;/span&gt;(&lt;span style="color: blue;"&gt;this&lt;/span&gt;)&lt;br/&gt;                        {&lt;br/&gt;                            &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mGn.MaxIdleTime == -1)&lt;br/&gt;                            {&lt;br/&gt;                                &lt;span style="color: #2b91af;"&gt;Monitor&lt;/span&gt;.Wait(&lt;span style="color: blue;"&gt;this&lt;/span&gt;);&lt;br/&gt;                            }&lt;br/&gt;                            &lt;span style="color: blue;"&gt;else&lt;br/&gt;                            &lt;/span&gt;{&lt;br/&gt;                                &lt;span style="color: #2b91af;"&gt;Monitor&lt;/span&gt;.Wait(&lt;span style="color: blue;"&gt;this&lt;/span&gt;, mGn.MaxIdleTime);&lt;br/&gt;                            }&lt;br/&gt;                        }&lt;br/&gt;                    }&lt;br/&gt;                    &lt;span style="color: blue;"&gt;catch&lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;                    {&lt;br/&gt;                        &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(ex.Message);&lt;br/&gt;                    }&lt;br/&gt;&lt;br/&gt;                    &lt;span style="color: blue;"&gt;try&lt;br/&gt;                    &lt;/span&gt;{&lt;br/&gt;                        &lt;span style="color: blue;"&gt;lock &lt;/span&gt;(mLock)&lt;br/&gt;                        {&lt;br/&gt;                            &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mGn.PendingJobs.Count == 0)&lt;br/&gt;                            {&lt;br/&gt;                                &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mGn.MinThreads != -1 &amp;amp;&amp;amp; mGn.AvailableThreads.Count &amp;gt; mGn.MinThreads)&lt;br/&gt;                                {&lt;br/&gt;                                    mGn.RemoveThread();&lt;br/&gt;                                    &lt;span style="color: blue;"&gt;return&lt;/span&gt;;&lt;br/&gt;                                }&lt;br/&gt;                            }&lt;br/&gt;                        }&lt;br/&gt;                    }&lt;br/&gt;                    &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;                    {&lt;br/&gt;                        &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(ex.Message);&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;            &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(ex.Message);&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; GenPool 类执行所有的阻塞线程，一旦线程的任务完成，那么在超过MaxIdleTime 时间后就会将符合条件的线程移除。Run() 方法开启一个循环来尝试从线程池中找到与当前线程匹配的线程，并执行它。通过代码你可以发现它会锁住一个对象，如果没有阻塞任务的话，那么它仅仅需要找到线程池中与当前线程匹配的线程，返回-1表示没有找到。如果有一个阻塞任务，那么它会返回第一个阻塞任务并将其从队列中移除。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在这一章，我们已经了解了线程池可以如何应用在运行时间比较短的线程上面。线程池技术允许回收线程。一个线程关联到一个任务上，当那个任务完成以后，线程会返回到线程池中并等待下一次任务。我们也介绍了在.NET 中使用线程池的几个方面。我们开始定义了什么是一个线程池以及我们为什么要在应用程序中使用线程池。我们也介绍了CLR 在创建线程池过程中扮演的角色和在使用线程池中可能遇到的问题。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 我们稍后介绍了一些扩展性问题，ThreadPool 类不适用于长时间运行的任务。我们讨论了如何创建一个ThreadPool 管理器以及SMP系统是如何通过使用线程提高应用程序的性能的。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;下一篇我们介绍 C# 线程手册 第六章 线程调试与跟踪&amp;hellip;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/danielWise/aggbug/2458520.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/danielWise/archive/2012/04/19/2458520.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/danielWise/archive/2012/04/15/2450221.html</id><title type="text">C# 线程手册 第五章 多线程应用程序 一个多线程微软消息队列(MSMQ)监听器</title><summary type="text">在这部分，我们将使用ThreadPool 和MSMQ 进行消息收发。MSMQ 是一个分布式队列，通过MSMQ 一个应用程序可以异步地与另外一个应用程序通信。 在一个典型的场景中，我们要向维护一个队列的MSMQ 服务器发送消息，MSMQ 发送方与MSMQ 服务器(特定队列)之间创建一个连接并向那个队列发送消息。一个MSMQ 接收器接收由MSMQ发送方发送的消息。MSMQ 接收方需要监听一个特定的队列以接收发送到这个队列上的消息。MSMQ服务器在MSMQ发送方和接收方之间起到了一个中转的作用，但MSMQ发送方不知道还有一个MSMQ接收方，反之亦然。 在我们的程序中，我们将开发一个MSMQ发送程序.</summary><published>2012-04-15T07:16:00Z</published><updated>2012-04-15T07:16:00Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><link rel="alternate" href="http://www.cnblogs.com/danielWise/archive/2012/04/15/2450221.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/archive/2012/04/15/2450221.html"/><content type="html">&lt;p&gt;&amp;nbsp; 在这部分，我们将使用ThreadPool 和MSMQ 进行消息收发。MSMQ 是一个分布式队列，通过MSMQ 一个应用程序可以异步地与另外一个应用程序通信。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在一个典型的场景中，我们要向维护一个队列的MSMQ 服务器发送消息，MSMQ 发送方与MSMQ 服务器(特定队列)之间创建一个连接并向那个队列发送消息。一个MSMQ 接收器接收由MSMQ发送方发送的消息。MSMQ 接收方需要监听一个特定的队列以接收发送到这个队列上的消息。MSMQ服务器在MSMQ发送方和接收方之间起到了一个中转的作用，但MSMQ发送方不知道还有一个MSMQ接收方，反之亦然。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在我们的程序中，我们将开发一个MSMQ发送程序(Windows 窗体应用)和一个MSMQ接收程序(控制台应用)。在我们的MSMQ发送方应用程序中(MSMQUI.cs), 我们使用ThreadPool 向队列中发送一条消息：&lt;/p&gt;&lt;span style="color: blue;"&gt;private void &lt;/span&gt;SendButton_Click(&lt;span style="color: blue;"&gt;object &lt;/span&gt;sender, &lt;span style="color: #2b91af;"&gt;EventArgs &lt;/span&gt;e)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;int &lt;/span&gt;count = &lt;span style="color: #2b91af;"&gt;Convert&lt;/span&gt;.ToInt32(countTxt.Text);&lt;br/&gt;    &lt;span style="color: blue;"&gt;while &lt;/span&gt;(count &amp;gt; 0)&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.QueueUserWorkItem(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitCallback&lt;/span&gt;(SendMessage), countTxt.Text);&lt;br/&gt;        count--;&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 在上面的代码片段中(MSMQUI.cs), 我们创建了一个计数器来向MSMQ发送消息。我们通过一个静态对象将消息传递给ThreadPool 并传递给WaitCallback 委托一个SendMessage() 方法的引用。换句话说，SendMessage() 方法将会给MSMQ 发送消息。&lt;/p&gt;&lt;span style="color: blue;"&gt;private void &lt;/span&gt;SendMessage(&lt;span style="color: blue;"&gt;object &lt;/span&gt;state)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;if &lt;/span&gt;(mq != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;br/&gt;    {&lt;br/&gt;        &lt;span style="color: blue;"&gt;try&lt;br/&gt;        &lt;/span&gt;{&lt;br/&gt;            msg.Body = state.ToString();&lt;br/&gt;            mq.Send(msg);&lt;br/&gt;        }&lt;br/&gt;        &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;InvalidCastException &lt;/span&gt;ex)&lt;br/&gt;        {&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; SendMessage() 方法把静态对象转成字符串并发送给MSMQ。&lt;/p&gt;&lt;p&gt;&amp;nbsp; MSMQUI 应用程序运行起来与如下截图类似：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204151515423979.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-15 15-10-36" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204151515434602.png" alt="2012-4-15 15-10-36" width="281" height="255" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在MSMQ 监听器(MSMQListener.cs)中, 当一条消息到达队列中以后我们从MSMQ服务端异步地接收消息通知。由于这个原因，我们创建了MSMQ 事件处理器并把MessageReceived() 方法名传递给ReceiveCompletedEventHandler 委托。&lt;/p&gt;mq1 = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;MessageQueue&lt;/span&gt;(&lt;span style="color: #a31515;"&gt;@".\private$\myfirstq3"&lt;/span&gt;);&lt;br/&gt;mq1.ReceiveCompleted += &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ReceiveCompletedEventHandler&lt;/span&gt;(MessageReceived);&lt;p&gt;&amp;nbsp; 为了开始从MSMQ 接收消息，我们需要在消息队列对象上调用BeginReceive() 方法。&lt;/p&gt;mq1.BeginReceive(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;TimeSpan&lt;/span&gt;(0,0,2));&lt;p&gt;&amp;nbsp; BeginReceive() 方法有一个TimeSpan 参数，它用来指示监听器在消息到达队列之前应该等待多长时间。如果没有使用TimeSpan参数，应用程序将会阻塞在BeginReceive() 方发处直到消息到达MSMQ。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 当一条消息到达队列后，MessageReceived() 方法由MSMQ事件处理器调用：&lt;/p&gt;&lt;span style="color: blue;"&gt;private static void &lt;/span&gt;MessageReceived(&lt;span style="color: blue;"&gt;object &lt;/span&gt;source, &lt;span style="color: #2b91af;"&gt;ReceiveCompletedEventArgs &lt;/span&gt;asyncResult)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;bool &lt;/span&gt;isReceivedSucceed = &lt;span style="color: blue;"&gt;true&lt;/span&gt;;&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;MessageQueue &lt;/span&gt;mq = &lt;span style="color: blue;"&gt;null&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: blue;"&gt;try&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: green;"&gt;//Connect to the queue&lt;br/&gt;        &lt;/span&gt;mq = (&lt;span style="color: #2b91af;"&gt;MessageQueue&lt;/span&gt;)source;&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Message &lt;/span&gt;m = mq.EndReceive(asyncResult.AsyncResult);&lt;br/&gt;        m.Formatter = &lt;span style="color: blue;"&gt;new &lt;/span&gt;System.Messaging.&lt;span style="color: #2b91af;"&gt;XmlMessageFormatter&lt;/span&gt;(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Type&lt;/span&gt;[] { &lt;span style="color: blue;"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue;"&gt;string&lt;/span&gt;) });&lt;br/&gt;        &lt;span style="color: green;"&gt;//Process the message here.&lt;br/&gt;        &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(m.Body.ToString());&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.QueueUserWorkItem(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitCallback&lt;/span&gt;(InvokeMDAO), m.Body);&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;MessageQueueException&lt;/span&gt;)&lt;br/&gt;    {&lt;br/&gt;        isReceivedSucceed = &lt;span style="color: blue;"&gt;false&lt;/span&gt;;&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;catch &lt;/span&gt;(&lt;span style="color: #2b91af;"&gt;Exception &lt;/span&gt;ex)&lt;br/&gt;    {&lt;br/&gt;        isReceivedSucceed = &lt;span style="color: blue;"&gt;false&lt;/span&gt;;&lt;br/&gt;        &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(ex.Message);&lt;br/&gt;    }&lt;br/&gt;    &lt;span style="color: blue;"&gt;finally&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;if &lt;/span&gt;(isReceivedSucceed)&lt;br/&gt;        {&lt;br/&gt;            mq1.BeginReceive(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;TimeSpan&lt;/span&gt;(0, 0, 2));&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 为了获取接收到的消息，我们需要在消息队列对象上调用EndReceive() 方法。在我们获取到System.Messaging.Message 对象后，如何处理这个对象就完全取决于我们了。在一个现实世界应用程序中，我们通常会调用System.EnterpriseServices(COM + )对象来进行一些数据库更新操作或者将消息传递给其他对象处理。这里我们可以使用ThreadPool 来保证对象调用可控。我们把消息体放到ThreadPool 中并把InvokeMDAO() 方法名传递给WaitCallback 委托。InvokeMDAO() 方法调用对象上的一个方法并在控制台上打印消息。如之前讨论的那样，我们可以在InvokeMDAO() 方法中调用一个System.EnterpriseServices(COM+)组件。这将直接控制创建的COM+对象数量。&lt;/p&gt;&lt;p&gt;下一篇介绍.NET 中的扩展性&amp;hellip;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/danielWise/aggbug/2450221.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/danielWise/archive/2012/04/15/2450221.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/danielWise/archive/2012/04/09/2439616.html</id><title type="text">C# 线程手册 第五章 扩展多线程应用程序 在C#中使用线程池</title><summary type="text">本章的之前部分内容主要介绍如何在.NET Framework 中使用线程池的概念。现在我们要介绍如何使用C# 实现创建并使用线程池的.NET 应用程序。如之前描述的那样，System.Threading 命名空间中包含的ThreadPool 类可以被用于在.NET 应用程序中创建一个线程池。 在我们真正编码之前，我们必须对ThreadPool 类中的两个重要规则非常清楚。分别是： 1. 每个应用程序域中只能有一个ThreadPool 对象 2. 我们第一次调用ThreadPool.QueueUserWorkItem() 方法时会创建一个ThreadPool 对象，通过一个定时器或者注册的等待.</summary><published>2012-04-09T14:08:00Z</published><updated>2012-04-09T14:08:00Z</updated><author><name>DanielWise</name><uri>http://www.cnblogs.com/danielWise/</uri></author><link rel="alternate" href="http://www.cnblogs.com/danielWise/archive/2012/04/09/2439616.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/danielWise/archive/2012/04/09/2439616.html"/><content type="html">&lt;p&gt;&amp;nbsp; 本章的之前部分内容主要介绍如何在.NET Framework 中使用线程池的概念。现在我们要介绍如何使用C# 实现创建并使用线程池的.NET 应用程序。如之前描述的那样，System.Threading 命名空间中包含的ThreadPool 类可以被用于在.NET 应用程序中创建一个线程池。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 在我们真正编码之前，我们必须对ThreadPool 类中的两个重要规则非常清楚。分别是：&lt;/p&gt;&lt;p&gt;&amp;nbsp; 1. 每个应用程序域中只能有一个ThreadPool 对象&lt;/p&gt;&lt;p&gt;&amp;nbsp; 2. 我们第一次调用ThreadPool.QueueUserWorkItem() 方法时会创建一个ThreadPool 对象，通过一个定时器或者注册的等待操作调用的回调方法(内部使用应用程序域的线程池)也可以创建一个ThreadPool 对象。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 首先，让我们通过几个例子来看一下为什么线程池比单独开启一个线程要好。在第一个例子(ThreadDemo.cs)中我们将使用独立线程(相对于线程池来说，以下类同)来启动两个长时间运行任务，而在第二个例子(ThreadPoolDemo.cs )中我们将使用一个线程池启动两个同样的任务:&lt;/p&gt;&lt;span style="color: green;"&gt;/*************************************&lt;br/&gt;/* Copyright (c) 2012 Daniel Dong&lt;br/&gt; * &lt;br/&gt; * Author：Daniel Dong&lt;br/&gt; * Blog：  www.cnblogs.com/danielWise&lt;br/&gt; * Email： guofoo@163.com&lt;br/&gt; * &lt;br/&gt; */&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Linq;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Text;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Threading;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;namespace &lt;/span&gt;ThreadDemo&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadDemo&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;public void &lt;/span&gt;LongTask1()&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: blue;"&gt;for &lt;/span&gt;(&lt;span style="color: blue;"&gt;int &lt;/span&gt;i = 0; i &amp;lt;= 999; i++)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Long Task 1 is being executed"&lt;/span&gt;);&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;public void &lt;/span&gt;LongTask2()&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: blue;"&gt;for &lt;/span&gt;(&lt;span style="color: blue;"&gt;int &lt;/span&gt;i = 0; i &amp;lt;= 999; i++)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Long Task 2 is being executed"&lt;/span&gt;);&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ThreadDemo &lt;/span&gt;td = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadDemo&lt;/span&gt;();&lt;br/&gt;            &lt;span style="color: blue;"&gt;for &lt;/span&gt;(&lt;span style="color: blue;"&gt;int &lt;/span&gt;i = 0; i &amp;lt; 10; i++)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Thread &lt;/span&gt;t1 = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadStart&lt;/span&gt;(td.LongTask1));&lt;br/&gt;                t1.Start();&lt;br/&gt;&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Thread &lt;/span&gt;t2 = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;Thread&lt;/span&gt;(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadStart&lt;/span&gt;(td.LongTask2));&lt;br/&gt;                t2.Start();&lt;br/&gt;            }&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.Read();&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 在上面的例子中，我们使用独立线程t1 和 t2 启动了两个任务LongTask1 和 LongTask2. 需要注意的是我们在一个循环中重复调用线程，目的是为了对操作系统处理能力施压，通过结果对比，我们可以清楚地看到使用线程池的优势。下面的ThreadPoolDemo 类显示了使用ThreadPool 类的代码：&lt;/p&gt;&lt;span style="color: green;"&gt;/*************************************&lt;br/&gt;/* Copyright (c) 2012 Daniel Dong&lt;br/&gt; * &lt;br/&gt; * Author：Daniel Dong&lt;br/&gt; * Blog：  www.cnblogs.com/danielWise&lt;br/&gt; * Email： guofoo@163.com&lt;br/&gt; * &lt;br/&gt; */&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Linq;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Text;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Threading;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;namespace &lt;/span&gt;ThreadPoolDemo&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadPoolDemo&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;public void &lt;/span&gt;LongTask1(&lt;span style="color: blue;"&gt;object &lt;/span&gt;obj)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: blue;"&gt;for &lt;/span&gt;(&lt;span style="color: blue;"&gt;int &lt;/span&gt;i = 0; i &amp;lt;= 999; i++)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Long Task 1 is being executed"&lt;/span&gt;);&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;public void &lt;/span&gt;LongTask2(&lt;span style="color: blue;"&gt;object &lt;/span&gt;obj)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: blue;"&gt;for &lt;/span&gt;(&lt;span style="color: blue;"&gt;int &lt;/span&gt;i = 0; i &amp;lt;= 999; i++)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Long Task 2 is being executed"&lt;/span&gt;);&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ThreadPoolDemo &lt;/span&gt;tpd = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadPoolDemo&lt;/span&gt;();&lt;br/&gt;            &lt;span style="color: blue;"&gt;for &lt;/span&gt;(&lt;span style="color: blue;"&gt;int &lt;/span&gt;i = 0; i &amp;lt; 10; i++)&lt;br/&gt;            {&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.QueueUserWorkItem(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitCallback&lt;/span&gt;(tpd.LongTask1));&lt;br/&gt;                &lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.QueueUserWorkItem(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitCallback&lt;/span&gt;(tpd.LongTask2));&lt;br/&gt;            }&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.Read();&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 让我们讨论一下上面的例子。它包括两个独立的任务LongTask1 和 LongTask2, 这两个任务都是简单地在一个循环中向控制台显示消息。将任务操作过程委托给WaitCallback() 方法可以免除为每个独立任务设置线程属性的过程并可以使用ThreadPool 启动这些任务， 具体请看下面的代码块：&lt;/p&gt;&lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.QueueUserWorkItem(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitCallback&lt;/span&gt;(tpd.LongTask1));&lt;br/&gt;&lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.QueueUserWorkItem(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitCallback&lt;/span&gt;(tpd.LongTask2));&lt;p&gt;&amp;nbsp; 注意QueueUserWorkItem 是ThreadPool 类中的一个静态方法因而可以由ThreadPool类直接调用。这个例子也有一个Console.Read() 语句，它会让控制台一直等待用户输入回车键或其他任意键。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 通过一个接一个地运行ThreadDemo 和 ThreadPoolDemo 应用程序，我们可以使用任务管理器对两者进行比较。取决于操作系统的处理能力所以在每个操作系统上结果都可能不同，但是相对结果将会是一样的。&lt;/p&gt;&lt;p&gt;&amp;nbsp; ThreadDemo 应用程序使用的线程数目图示：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204092207542703.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-9 21-41-05" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204092207557031.png" alt="2012-4-9 21-41-05" width="513" height="239" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; ThreadPoolDemo 应用程序使用的线程数目图示：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204092207567522.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-9 21-42-48" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204092207582340.png" alt="2012-4-9 21-42-48" width="514" height="176" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 通过两个截图的对比，我可以可以很明显地发现使用ThreadPool 不仅可以帮助降低应用程序使用的线程数量还可以减小CPU时间和应用程序使用的内存大小。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 下一个例子显示了如何向一个线程池中的线程传递参数以及接收它的返回值。线程池架构仅允许我们传递一个单一对象参数，但是通常我们向给被一个线程池中的线程执行的方法传递多个参数。然而，我们可以很容易地将所有必要的参数包装到一个类中并将类的实例作为一个参数传递给QueueUserWorkItem() 方法：&lt;/p&gt;&lt;span style="color: green;"&gt;/*************************************&lt;br/&gt;/* Copyright (c) 2012 Daniel Dong&lt;br/&gt; * &lt;br/&gt; * Author：Daniel Dong&lt;br/&gt; * Blog：  www.cnblogs.com/danielWise&lt;br/&gt; * Email： guofoo@163.com&lt;br/&gt; * &lt;br/&gt; */&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Linq;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Text;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Threading;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;namespace &lt;/span&gt;ThreadPoolState&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;class &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadPoolState&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;public void &lt;/span&gt;Task1(&lt;span style="color: blue;"&gt;object &lt;/span&gt;stateObj)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ObjState &lt;/span&gt;stObj = (&lt;span style="color: #2b91af;"&gt;ObjState&lt;/span&gt;)stateObj;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Input Argument 1 in task 1: " &lt;/span&gt;+ stObj.inarg1);&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Input Argument 2 in task 1: " &lt;/span&gt;+ stObj.inarg2);&lt;br/&gt;            stObj.outval = &lt;span style="color: #a31515;"&gt;"From Task 1 " &lt;/span&gt;+ stObj.inarg1 + &lt;span style="color: #a31515;"&gt;" " &lt;/span&gt;+ stObj.inarg2;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;public void &lt;/span&gt;Task2(&lt;span style="color: blue;"&gt;object &lt;/span&gt;stateObj)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ObjState &lt;/span&gt;stObj = (&lt;span style="color: #2b91af;"&gt;ObjState&lt;/span&gt;)stateObj;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Input Argument 1 in task 2: " &lt;/span&gt;+ stObj.inarg1);&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Input Argument 2 in task 2: " &lt;/span&gt;+ stObj.inarg2);&lt;br/&gt;            stObj.outval = &lt;span style="color: #a31515;"&gt;"From Task 2 " &lt;/span&gt;+ stObj.inarg1 + &lt;span style="color: #a31515;"&gt;" " &lt;/span&gt;+ stObj.inarg2;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ObjState &lt;/span&gt;stObj1 = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ObjState&lt;/span&gt;();&lt;br/&gt;            stObj1.inarg1 = &lt;span style="color: #a31515;"&gt;"String Param1 of task 1"&lt;/span&gt;;&lt;br/&gt;            stObj1.inarg2 = &lt;span style="color: #a31515;"&gt;"String Param2 of task 1"&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ObjState &lt;/span&gt;stObj2 = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ObjState&lt;/span&gt;();&lt;br/&gt;            stObj2.inarg1 = &lt;span style="color: #a31515;"&gt;"String Param1 of task 2"&lt;/span&gt;;&lt;br/&gt;            stObj2.inarg2 = &lt;span style="color: #a31515;"&gt;"String Param2 of task 2"&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ThreadPoolState &lt;/span&gt;tps = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadPoolState&lt;/span&gt;();&lt;br/&gt;            &lt;span style="color: green;"&gt;//Queue a task&lt;br/&gt;            &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.QueueUserWorkItem(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitCallback&lt;/span&gt;(tps.Task1), stObj1);&lt;br/&gt;            &lt;span style="color: green;"&gt;//Queue another task&lt;br/&gt;            &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.QueueUserWorkItem(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitCallback&lt;/span&gt;(tps.Task2), stObj2);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.Read();&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;&amp;nbsp; ThreadPoolState 的输出结果如下：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204092207585862.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-9 22-05-11" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204092207593288.png" alt="2012-4-9 22-05-11" width="455" height="111" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 我们现在来一步一步地分析上面的例子。这个例子与之前的例子非常类似除了传递了一个对象；我们使用ObjState 对象向线程池的任务队列传递输入和输出参数。&lt;/p&gt;&lt;p&gt;&amp;nbsp; ObjState 对象包含两个输入参数和一个输出参数，所有类型都是String, 如下面代码块显示：&lt;/p&gt;&lt;span style="color: blue;"&gt;internal class &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ObjState&lt;br/&gt;&lt;/span&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;protected internal &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;String &lt;/span&gt;inarg1;&lt;br/&gt;    &lt;span style="color: blue;"&gt;protected internal &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;String &lt;/span&gt;inarg2;&lt;br/&gt;    &lt;span style="color: blue;"&gt;protected internal &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;String &lt;/span&gt;outval;&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 下一步我们定义了两个方法，task1 和 task2, 并为它们分别传递了一个ObjState 对象的实例作为参数。task1 和 task2 将输入参数对象的inarg1 和 inarg2 值组合起来并将结果存储到outval 变量中。如下面代码块所示：&lt;/p&gt;&lt;span style="color: blue;"&gt;public void &lt;/span&gt;Task1(&lt;span style="color: blue;"&gt;object &lt;/span&gt;stateObj)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;ObjState &lt;/span&gt;stObj = (&lt;span style="color: #2b91af;"&gt;ObjState&lt;/span&gt;)stateObj;&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Input Argument 1 in task 1: " &lt;/span&gt;+ stObj.inarg1);&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Input Argument 2 in task 1: " &lt;/span&gt;+ stObj.inarg2);&lt;br/&gt;    stObj.outval = &lt;span style="color: #a31515;"&gt;"From Task 1 " &lt;/span&gt;+ stObj.inarg1 + &lt;span style="color: #a31515;"&gt;" " &lt;/span&gt;+ stObj.inarg2;&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;public void &lt;/span&gt;Task2(&lt;span style="color: blue;"&gt;object &lt;/span&gt;stateObj)&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;ObjState &lt;/span&gt;stObj = (&lt;span style="color: #2b91af;"&gt;ObjState&lt;/span&gt;)stateObj;&lt;br/&gt;&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Input Argument 1 in task 2: " &lt;/span&gt;+ stObj.inarg1);&lt;br/&gt;    &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Input Argument 2 in task 2: " &lt;/span&gt;+ stObj.inarg2);&lt;br/&gt;    stObj.outval = &lt;span style="color: #a31515;"&gt;"From Task 2 " &lt;/span&gt;+ stObj.inarg1 + &lt;span style="color: #a31515;"&gt;" " &lt;/span&gt;+ stObj.inarg2;&lt;br/&gt;}&lt;p&gt;&amp;nbsp; 在Main() 方法中我们使用ThreadPool.QueueUserWorkItem() 方法在线程池中运行这两个任务，如下代码块所示：&lt;/p&gt;&lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ObjState &lt;/span&gt;stObj1 = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ObjState&lt;/span&gt;();&lt;br/&gt;            stObj1.inarg1 = &lt;span style="color: #a31515;"&gt;"String Param1 of task 1"&lt;/span&gt;;&lt;br/&gt;            stObj1.inarg2 = &lt;span style="color: #a31515;"&gt;"String Param2 of task 1"&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ObjState &lt;/span&gt;stObj2 = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ObjState&lt;/span&gt;();&lt;br/&gt;            stObj2.inarg1 = &lt;span style="color: #a31515;"&gt;"String Param1 of task 2"&lt;/span&gt;;&lt;br/&gt;            stObj2.inarg2 = &lt;span style="color: #a31515;"&gt;"String Param2 of task 2"&lt;/span&gt;;&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ThreadPoolState &lt;/span&gt;tps = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadPoolState&lt;/span&gt;();&lt;br/&gt;            &lt;span style="color: green;"&gt;//Queue a task&lt;br/&gt;            &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.QueueUserWorkItem(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitCallback&lt;/span&gt;(tps.Task1), stObj1);&lt;br/&gt;            &lt;span style="color: green;"&gt;//Queue another task&lt;br/&gt;            &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.QueueUserWorkItem(&lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitCallback&lt;/span&gt;(tps.Task2), stObj2);&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.Read();&lt;br/&gt;        }&lt;p&gt;&amp;nbsp; 我们也可以使用ThreadPool.RegisterWaitForSingleObject() 方法来运行有等待操作的任务，这种情况下需要将任务的等待操作传递给WaitHandle. WaitHandle 通知包装到一个WaitOrTimerCallback 委托的方法。在这种情况下，线程池创建一个后台线程调用回调方法。下面的代码(RegWait.cs)描述了这个概念：&lt;/p&gt;&lt;span style="color: green;"&gt;/*************************************&lt;br/&gt;/* Copyright (c) 2012 Daniel Dong&lt;br/&gt; * &lt;br/&gt; * Author：Daniel Dong&lt;br/&gt; * Blog：  www.cnblogs.com/danielWise&lt;br/&gt; * Email： guofoo@163.com&lt;br/&gt; * &lt;br/&gt; */&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Linq;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Text;&lt;br/&gt;&lt;span style="color: blue;"&gt;using &lt;/span&gt;System.Threading;&lt;br/&gt;&lt;br/&gt;&lt;span style="color: blue;"&gt;namespace &lt;/span&gt;RegWait&lt;br/&gt;{&lt;br/&gt;    &lt;span style="color: blue;"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;RegWait&lt;br/&gt;    &lt;/span&gt;{&lt;br/&gt;        &lt;span style="color: blue;"&gt;private static int &lt;/span&gt;i = 0;&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;static void &lt;/span&gt;Main(&lt;span style="color: blue;"&gt;string&lt;/span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;AutoResetEvent &lt;/span&gt;arev = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;AutoResetEvent&lt;/span&gt;(&lt;span style="color: blue;"&gt;false&lt;/span&gt;);&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.RegisterWaitForSingleObject(&lt;br/&gt;                arev, &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitOrTimerCallback&lt;/span&gt;(WorkItem), &lt;span style="color: blue;"&gt;null&lt;/span&gt;, 2000, &lt;span style="color: blue;"&gt;false&lt;/span&gt;);&lt;br/&gt;            arev.Set();&lt;br/&gt;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.Read();&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        &lt;span style="color: blue;"&gt;public static void &lt;/span&gt;WorkItem(&lt;span style="color: blue;"&gt;object &lt;/span&gt;o, &lt;span style="color: blue;"&gt;bool &lt;/span&gt;signaled)&lt;br/&gt;        {&lt;br/&gt;            i += 1;&lt;br/&gt;            &lt;span style="color: #2b91af;"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515;"&gt;"Thread Pool Work Item Invoked: " &lt;/span&gt;+ i.ToString());&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;上面例子的输入结果与下面类似：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204092208003504.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="2012-4-9 22-06-17" src="http://images.cnblogs.com/cnblogs_com/danielWise/201204/201204092208012566.png" alt="2012-4-9 22-06-17" width="398" height="107" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; 每个两秒会在控制台打印一行新的结果并输出递增变量i 的值，直到用户输入回车键调用Console.Read() 方法退出为止。&lt;/p&gt;&lt;p&gt;&amp;nbsp; 程序一开始会创建一个名为arec 初始值为non-signaled 的AutoResetEvent 对象来通知线程池执行任务组件：&lt;/p&gt;&lt;span style="color: #2b91af;"&gt;AutoResetEvent &lt;/span&gt;arev = &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;AutoResetEvent&lt;/span&gt;(&lt;span style="color: blue;"&gt;false&lt;/span&gt;);&lt;p&gt;&amp;nbsp; 我们调用RegisterWaitForSingleObject() 方法，参数State 值为null, timeout 值为2000毫秒，executeOnceOnly 为false. RegisterWaitForSingleObject() 注册一个委托并在指定的时间间隔通知工作组件。在我们的例子中，时间间隔设置为两秒，如下代码所示：&lt;/p&gt;&lt;span style="color: #2b91af;"&gt;ThreadPool&lt;/span&gt;.RegisterWaitForSingleObject(&lt;br/&gt;    arev, &lt;span style="color: blue;"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af;"&gt;WaitOrTimerCallback&lt;/span&gt;(WorkItem), &lt;span style="color: blue;"&gt;null&lt;/span&gt;, 2000, &lt;span style="color: blue;"&gt;false&lt;/span&gt;);&lt;p&gt;&amp;nbsp; 为了触发事件我们需要使用AutoResetEvent对象的Set() 方法：&lt;/p&gt;arev.Set();&lt;p&gt;&amp;nbsp; 这个例子包含了在C# 程序中如何使用线程池的部分内容；下一部分我们将检查线程池的可扩展性并创建一个线程池管理应用程序。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;下一篇介绍一个多线程的微软消息队列(MSMQ)监听器&amp;hellip;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/danielWise/aggbug/2439616.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/danielWise/archive/2012/04/09/2439616.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
