ASP.NET中的多线程(译)

问题
 性能和响应性是网络应用程序成功的关键话题。不要寄期望于用户的耐心。通常他们会迅速关闭反应迟钝或者僵死的程序
 有时你需要长时间执行一个占用大量cpu操作的任务,有时你从数据库等待结果,更不用说执行繁重的I/O操作。这种情况下,结果常常是你的程序一直处于“假死”状态,直到这些耗时的操作完成。
 解决这个问题的一般方案是使用多线程。.net框架提供了一个允许我们通过多线程和异步编程技术来解决这个问题的类库。你可以使用线程池来获得新线程,或者你可以手动创建,这取决于你想对新建线程的控制程度。
 本指南中我们讨论使用线程池的多线程编程技术
 
什么是多线程?
 多线程是用来在一个单独的附加线程而非主应用程序线程中执行耗时任务的一项技术。
 例如,你有一个非常耗时的函数,你可能需要在点击一个按钮时调用它。现在你可以创建一个新的线程来运行这个函数而不是在这个函数返回或者结束之前冻结你的应用程序。这样做的时候,你的应用程序界面将不会被阻塞,并且你可以用它来执行其他任务。同时,你的耗时的任务在后台被执行!
 你可以把它看成两个线程:主线程和新建的线程。他们并行运行,而且这将提高你应用程序的性能和相应性。
 
使用多线程的优势和弊端
 尽管可以提高程序的性能并且避免无相应的用户界面,多线程有以下不足:
  创建和销毁线程要需要系统开销。当你的应用程序频繁的创建和销毁线程,这种开销将会影响应用程序的总体性能
  同时执行太多线程会降低整体系统的性能。因为系统将试图为每一个线程分配一个时间片
  使用多线程的时候你需要很好的设计你的程序,否则它将会很难以维护或者扩展
  执行多线程程序的时候你需要特别小心,因为线程bug很难以调试和解决
  
注意:
 每个线程被创建的时候都会消耗一定的内存来维持此线程的上下文信息。因此,可以创建的线程数量受可用内存的限制
 更多的线程并不意味着一个响应更快的程序,相反那会降低程序的性能
 
线程池
 除了在每次使用线程的时候它创建结束的时候销毁,.net框架引入了线程池的概念。线程池技术中,需要的时候程序会从线程池中获取一个线程而不是创建一个新线程,线程完成它的任务后将被返回到线程池中,而不是销毁它——等待下次使用。这种重用性提升了程序的总体性能,并且减少了线程创建和终止的额外开销
 为了使用这种技术,你需要使用System.Threading.ThreadPool类。线程池会在你第一次使用它的时候被创建。线程池中的线程总量被默认限制为25个。也就是说所有这25个线程可能在某个时候都处于忙碌状态,而如果此时需要获取一个新线程,你的任务将等待一个线程结束并且返回到线程池
 
如何使用线程池
 下载例程 http://www.beansoftware.com/ASP.NET-Tutorials/Examples/Multithreading.zip
 为了演示如何使用线程池技术来创建多线程程序,让我们创建一个新的网络应用程序。我们将一步一步创建
 打开vs2005,创建一个新的网络应用程序,在default.aspx 网页窗体(web form)中添加如下图所示控件:
 
        Figure 1 - Our web application in its design view
 通过双击按钮定位到按钮点击处理函数
 在default.aspx.vb文件中,引入System.Thread命名空间
   1 Imports System.Threading
 创建一个耗时的方法,它将在一个单独线程中在后台执行。为了将此方法指派到线程池,它必须和WaitCallBack代理有相同的签名。代理按如下方式声明:
   Public Delegate Sub WaitCallback(ByVal state As Object)
 这个代理表明一个被线程池中的线程执行的回调方法。参数state表明你前面创建的方法要用的信息,如果需要传递信息给它的话。
 我们的耗时的方法如下代码片断所示:
    17     Private Sub LongTimeTask(ByVal s As Object)  
    18   
    19         Dim i As Integer  
    20         Dim str As String  
    21         str = s.ToString  
    22   
    23         For i = 0 To 1000  
    24             str = str + "--" + str  
    25         Next  
    26   
    27     End Sub
 现在,在按钮点击处理函数处输入以下代码:
     5     Protected Sub Button1_Click(ByVal sender As _   
     6      Object, ByVal e As System.EventArgs) Handles Button1.Click   
     7    
     8         If ThreadPool.QueueUserWorkItem( _   
     9           New WaitCallback(AddressOf LongTimeTask), TextBox1.Text) _  
     10           Then  
     11             Label2.Text = "Queued successfully"  
     12         Else  
     13             Label2.Text = "Failed"  
     14         End If  
     15   
     16     End Sub
 在第8行代码中,我们使用ThreadPool.QueueUserWorkItem函数将我们“耗费时间的方法”加入队列,它将从线程池中取出并在一个单独的线程中执行.传递给这个函数的第一个参数是WaitCallBack代理的一个新实例,它表明要被执行的方法.在我们的例子中,要执行的方法是LongTimeTask.第二个是传递给LongTimeTask方法本身的参数.在我们的例子中,这个参数是终端用户在文本框中输入的文本.如果入队操作成功将返回true,否则失败的话将返回false.
 现在运行程序并且在文本框中输入任何文本,然后点击按钮,并且观察结果.
 可以在文本框中输入一段很长的文本或者增加循环的次数来更好的体会此操作.你也可以再添加一个新的文本框,这样你就可以在后台运行耗时任务的时候测试你用户界面的响应性
 就这样...
 正如你看到的,可以很便利地使用这项技术来解决那些恼人耗时任务的问题。注意,这种便利是有代价的。原因如下
 
使用线程池的优点
 线程池是用来创建多线程应用程序的最容易的技术,理由如下:
  你不需要创建、控制、安排以及中止线程,线程池类会为你做这些
  不需要担心创建过多线程并且因之影响系统性能。线程池的大小受.NET运行时限制,你可以使用的线程数量也同样受限
  你只要写较少的代码,因为.net框架在内部由经过很好检测并且没有bug的程序管理你的线程
 
 使用线程池的限制:
 尽管易于使用,和手动控制线程相比,线程池有如下限制或者不足
  使用线程池你没办法控制线程的状态和优先级
  使用线程吃你无法给线程一个稳定的ID并且对它保持跟踪
  当提交到线程池一个操作,你没办法确定什么时候操作将被执行。当你的操作需要被紧急执行的时候线程池可能会推迟它执行
  当你想通过两个线程运行两个任务或者操作,并且希望这两个任务以可确定的方式同时处理,线程池是不合适的
  .NET框架式哟国内线程池来执行异步操作,这对可用线程的数量限制有更多的要求
  Despite of robust application isolation,还是有些情况下你的应用程序代码可以被另外的程序代码影响

 当因为这些限制你不能使用线程池的时候,你可以手动创建线程并且自己管理它们。这种技术比线程池复杂的多,但它让你对线程有更多的控制。这是我们下一次的主题。

英文原文

原文地址:https://www.cnblogs.com/taoeternal/p/634051.html