• [Thread]“ThreadPool 对象中没有足够的自由线程来完成操作”的现象和解决办法


    编写者:郑昀@Ultrapower 20050406

    其实微软有一篇《异步 HttpWebRequest、接口实现及其他》对此种现象解释得非常清楚,我这边只是做一个笔记。

    最常见的就是使用HttpWebRequest的时候,调用Send方法出现这种错误,这是因为:

    因为dotNETThreadPool中提供了25个自由线程/CPU(可以在machine.config中修改此数字限制),所以一旦都被占用了,就会报告InvalidOperationException异常,异常提示为:

    System.InvalidOperationException: There were not enough free threads in the ThreadPool object to complete the operation

    微软的Stephen Toub是这么说的:

    需要知道的第一件事情是,在 Microsoft.NET Framework 1.x 版中,HttpWebRequest 从来不会发出同步请求。我这样说是什么意思呢?让我们来看一看 Shared Source CLI (SSCLI) 中为 HttpWebRequest.GetResponse 编写的代码,此处显示的代码省略了查看以前是否检索了该响应的代码和计算超时的代码:

    public override WebResponse GetResponse() {

        IAsyncResult asyncResult = BeginGetResponse(null, null);

        return EndGetResponse(asyncResult);

    }

    您可以看出,HttpWebRequest.GetResponse 只是 BeginGetResponse EndGetResponse 对周围的包装。它们是异步运行的,这意味着,BeginGetResponse 从中发出实际 HTTP 请求的线程不同于调用它的线程,而且在该请求完成之前,EndGetResponse 会阻塞。这样做的实际结果是,HttpWebRequest 将每个出站请求的 ThreadPool 排入工作项队列中。因此,我们知道 HttpWebRequest 使用来自 ThreadPool 的线程。

    作为该问题的替代解决方案,Framework 小组实现了您正在研究的异常。在 BeginGetResponse 的最后(就在工作项被排入队列之前),使用 System.Net.Connection.IsThreadPoolLow 来查询池中有多少个可用工作线程。如果数量过少(少于 2 个),将会引发 InvalidOperationException

    好消息是,在 .NET Framework 2.0 中,用 HttpWebRequest.GetResponse 发出的同步请求是真正同步的,这样该问题就不复存在了,从而使得您可以将调用 GetResponse 的方法列入您的核心内容。不太好的消息是,您仍然会受到 1.x 版本中这一问题的困扰。一种解决方案是(正如您所指出的),显式限制已经排入队列的工作项的数量或在任何时间内在 ThreadPool 中执行的工作项的数量。要实现这一方法,需要有一种方法来跟踪当前有多少个未完成的工作项,并在达到预定界限之前,阻塞新的请求。

     

    我们可以这么判断当前的自由线程数目:

    int wt;

    int ct;

    int Count=0;

    while(true)

    {              

    if(Count++>20)

    break;

    ThreadPool.GetAvailableThreads(out wt,out ct);

    if(wt<5)

    {

    Thread.Sleep(1000);

    continue;

    }

    else

    break;

    }

     

    除了这个HttpWebRequest问题之外,别的地方也会发生此问题。

    http://support.microsoft.com/default.aspx?scid=kb;en-us;815637

    微软演示了以下的程序:

        using System;

        using System.IO;

        using System.Net;

        using System.Text;

        using System.Threading;

        using System.Net.Sockets;

        namespace ThreadPoolException

        {

            class Class1

            {

                public static void Main()

                {

                    // Set number of threads to be created for testing.

                    int testThreads = 55;

                    for(int i=0;i<testThreads;i++)

                    {

                        ThreadPool.QueueUserWorkItem(new WaitCallback(PoolFunc));

                    }

                    Console.ReadLine();

                }

                static void PoolFunc(object state)

                {

                    int workerThreads,completionPortThreads;

                    ThreadPool.GetAvailableThreads(out workerThreads,

                        out completionPortThreads);

                    Console.WriteLine("WorkerThreads: {0}, CompletionPortThreads: {1}",

                        workerThreads, completionPortThreads);

                    Thread.Sleep(10000);

                    string url ="http://www.msn.com";

                    HttpWebRequest myHttpWebRequest ;

                    HttpWebResponse myHttpWebResponse=null ;

                    // Creates an HttpWebRequest for the specified URL.

                    myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);

                    // Sends the HttpWebRequest, and waits for a response.

                    myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();

                    myHttpWebResponse.Close();

                }

            }

        }

     

    程序输出如下:

    WorkerThreads: 9, CompletionPortThreads: 1000

    WorkerThreads: 8, CompletionPortThreads: 1000

    WorkerThreads: 7, CompletionPortThreads: 1000

    WorkerThreads: 6, CompletionPortThreads: 1000

    WorkerThreads: 5, CompletionPortThreads: 1000

    WorkerThreads: 4, CompletionPortThreads: 1000

    WorkerThreads: 3, CompletionPortThreads: 1000

    WorkerThreads: 2, CompletionPortThreads: 1000

    WorkerThreads: 1, CompletionPortThreads: 1000

    未处理的异常: System.InvalidOperationException: ThreadPool 对象中没有足够的自由

    线程来完成操作。

       at System.Net.HttpWebRequest.BeginGetResponse(AsyncCallback callback, Object

    state)

       at System.Net.HttpWebRequest.GetResponse()

       at ThreadPoolException.Class1.PoolFunc(Object state) in c:\threadpoolexception\threa

    dpoolexception\class1.cs:line 41

     

    编写者:郑昀@Ultrapower

  • 相关阅读:
    SQL Server 2005中的分区表(一):什么是分区表?为什么要用分区表?如何创建分区表?
    SQL Server 分区表
    SQL Server表分区
    Red Gate系列之一 SQL Compare 10.4.8.87 Edition 数据库比较工具 完全破解+使用教程
    C# WinForm开发系列
    C# WinForm开发系列
    富文本编辑器 CKeditor 配置使用
    C# ListView用法详解
    C# WinForm给Button或其它控件添加快捷键响应
    WebApi安全性 使用TOKEN+签名验证
  • 原文地址:https://www.cnblogs.com/zhengyun_ustc/p/135829.html
Copyright © 2020-2023  润新知