.NET CLR 线程池耗尽 - 实现错误?

2024-04-08

我编写了一个简单的基于异步的负载测试库,它还有一个控制台界面可以从命令行进行测试。

基本上,它同时运行大量请求,聚合它们,并显示摘要和简单的直方图。没有什么花哨。但我在本地系统中运行了大量测试,因此我想确保测试工具能够使用尽可能少的资源来获得相对准确的基准测试。因此它使用带有 Begin/End 方法的裸异步来保持最小的开销。

一切都完成了,完全异步,它可以工作,并且不会妨碍(好吧,大部分)。但正常会话中的线程数量远远超过 40 个。因此,考虑到本地计算机也运行正在测试的服务器,对于具有 4 个硬件线程的计算机来说,这确实是一种资源浪费。

我已经在 AsyncContext 中运行该程序,它基本上只是一个简单的排队上下文,将所有内容都放在同一个线程上。因此,所有 aync 回发都在主线程上。完美的。

现在,我所要做的就是限制 ThreadPool 的最大线程数,并看看它的性能如何。将其限制为实际核心,具有 4 个工作线程和 4 个 IOCP 线程。

Result?

异常:“线程池中没有足够的空闲线程 来完成操作。”

嗯,这不是一个新问题,并且在互联网上非常分散。但是,ThreadPool 的全部意义不就是您可以将事物放入池的队列中,并在线程可用时执行它吗?

事实上,该方法的名称是'Queue' 用户工作项。文件恰当地说:“将方法排队等待执行。当线程池线程可用时执行该方法."

现在,如果没有足够的可用线程,理想情况下,预期的情况可能是程序执行速度减慢。 IOCP、异步任务本来就应该排队,但是为什么会以这样的方式实现呢,反而会撞倒,失败呢?当线程池被称为队列时,增加线程数量并不是解决方案。

编辑-澄清:

我完全了解线程池的概念,以及为什么 CLR 启动更多线程。它应该。我同意,当存在繁重的 IO 密集型任务时,这实际上是正确的做法。但关键是,如果你确实限制 线程池中的线程,预计将任务排队 每当有空闲线程可用时执行,不抛出异常。 并发可能会受到影响,甚至可能减慢 结果,但 QueueWorkUserItem 旨在队列,而不仅仅是工作 当新线程可用或失败时 - 因此,我推测它是一个实现错误,如标题中所述。

更新1:

与 Microsoft 支持论坛中记录的相同问题并附有示例:http://support.microsoft.com/default.aspx?scid=kb;EN-US;815637 http://support.microsoft.com/default.aspx?scid=kb;EN-US;815637

建议的解决方法显然是增加线程数量,因为它无法排队。

注意:这是在非常旧的运行时下进行的,下面给出了在 4.5.1 运行时重现相同问题的方法。

更新2:

Ran Mono Runtime 上的相同代码段,并且 ThreadPool 似乎没有问题那里。它会排队并执行。该问题仅在 Microsoft CLR 下发生。

更新3:

在 @Noseratio 指出无法在 .NET 4.5.1 下重现相同代码的有效问题之后,下面是一段将重现该问题的代码。为了破坏按预期排队时工作的代码,真正需要做的就是向排队委托添加真正的异步调用。

例如,仅将以下行添加到委托的末尾应该会出现异常:

(await WebRequest.Create("http://www.google.com").GetResponseAsync()).Close(); 

复制代码:

下面的代码是根据 MSKB 文章稍作修改的,在 Windows 8.1 中的 .NET 4.5.1 下应该会很快失败。

(随意更改 url 和线程限制)。

public static void Main()
{
    ThreadPool.SetMinThreads(1, 1);
    ThreadPool.SetMaxThreads(2, 2);

    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine("Queued {0}", i);
        ThreadPool.QueueUserWorkItem(PoolFunc);
    }
    Console.ReadLine();
}

private static async void PoolFunc(object state)
{
    int workerThreads, completionPortThreads;
    ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
    Console.WriteLine(
        "Available: WorkerThreads: {0}, CompletionPortThreads: {1}",
        workerThreads,
        completionPortThreads);
    Thread.Sleep(1000);

    string url = "http://localhost:8080";

    HttpWebRequest myHttpWebRequest;
    // Creates an HttpWebRequest for the specified URL.    
    myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
    // Sends the HttpWebRequest, and waits for a response.
    Console.WriteLine("Wait for response.");
    var myHttpWebResponse = await myHttpWebRequest.GetResponseAsync();
    Console.WriteLine("Done.");
    myHttpWebResponse.Close();
}

任何对这种行为的洞察,可以对此进行推理,我们都非常感激。谢谢。


在您的示例代码中,它不是调用QueueUserWorkItem它抛出一个异常,它是对await myHttpWebRequest.GetResponseAsync()这会引发异常。如果您查看异常详细信息,您可以准确地看到是什么方法引发了此异常

System.InvalidOperationException was unhandled by user code
  _HResult=-2146233079
  _message=There were not enough free threads in the ThreadPool to complete the operation.
  HResult=-2146233079
  IsTransient=false
  Message=There were not enough free threads in the ThreadPool to complete the operation.
  Source=System
  StackTrace:
       at System.Net.HttpWebRequest.BeginGetResponse(AsyncCallback callback, Object state)
       at System.Threading.Tasks.TaskFactory`1.FromAsyncImpl(Func`3 beginMethod, Func`2 endFunction, Action`1 endAction, Object state, TaskCreationOptions creationOptions)
       at System.Threading.Tasks.TaskFactory`1.FromAsync(Func`3 beginMethod, Func`2 endMethod, Object state)
       at System.Net.WebRequest.<GetResponseAsync>b__8()
       at System.Threading.Tasks.Task`1.InnerInvoke()
       at System.Threading.Tasks.Task.Execute()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
       at ConsoleApplication1.Program.<PoolFunc>d__0.MoveNext() in c:\Users\Justin\Source\Repos\Azure\ConsoleApplication1\ConsoleApplication1\Program.cs:line 39
  InnerException: 

确实,如果我们看HttpWebRequest.BeginGetResponse method http://referencesource.microsoft.com/#System/net/System/Net/HttpWebRequest.cs#613ffcf7266252eb我们可以看到以下内容

if (!RequestSubmitted && NclUtilities.IsThreadPoolLow())
{
    // prevent new requests when low on resources
    Exception exception = new InvalidOperationException(SR.GetString(SR.net_needmorethreads));
    Abort(exception, AbortState.Public);
    throw exception;
}

这个故事的寓意是,线程池是其他代码(包括 .Net 框架的一部分)也使用的共享资源 - 将最大线程数设置为 2 就是 Raymond Chen 所说的局部问题的全局解决方案结果打破了系统其他部分的期望。

如果您想明确控制正在使用的线程,那么您应该创建自己的实现,但是除非您真的知道自己在做什么,否则最好让 .Net 框架处理线程管理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.NET CLR 线程池耗尽 - 实现错误? 的相关文章

  • 在 DataGridView 中隐藏行非常慢

    我在 Winforms 应用程序中有一个 DataGridView 大约有 1000 行 未绑定 和 50 列 隐藏一列需要整整 2 秒 当我想隐藏大约一半的行时 这就成为一个问题 private void ShowRows string
  • “包含字符串”的快速索引

    在我的应用程序中 我有多达数百万个短字符串 大部分短于 32 个字符 我想实现一个带有附加列表的搜索框 该列表仅包含包含在搜索框中输入的整个字符串的元素 如何预先建立索引来快速找到此类字符串 所有排序的 STL 容器都会检查整个字符串 对于
  • C++:将模板参数的模板类型成员添加为好友的正确语法?

    我有一个带有模板类型参数 tTRAIT 的类 我想加一个模板为好友type member aliastTRAIT 但我无法弄清楚语法 这可能吗 template
  • 在不使用 ncurses 的情况下用 C/C++ 编写“真正的”交互式终端程序,例如 vim、htop...

    不 我不想使用ncurses 因为我想了解如何 终端可以工作 并且我自己编程也很有趣 没有 必须是可移植的 它必须只能在基于 linux xterm 的终端仿真器上工作 我想做的是编写一个交互式终端应用程序 例如 htop 和 vim 我的
  • 如何使用boost库读取和写入.ini文件[重复]

    这个问题在这里已经有答案了 如何使用boost库读取和写入 或修改 ini文件 With Boost PropertyTree您可以读取并更新树 然后写入文件 请参阅load and save功能 看一下如何访问属性树中的数据 http w
  • WinForms - 表单大小错误

    我们有以下代码 private void MainForm Shown object sender EventArgs e RepositionForm private void RepositionForm Rectangle rect
  • 是否有一种算法可以在线性时间内计算数组反转?

    我知道有多少倒转 en wikipedia org wiki Inversion 28discrete mathematics 29 in an n 元素数组可以在 O n log n 操作使用增强型归并排序 http www geeksf
  • Qt QML 数据模型似乎不适用于 C++

    我一直在使用中的示例http doc qt digia com 4 7 qdeclarativemodels html http doc qt digia com 4 7 qdeclarativemodels html这是 QML 声明性数
  • 函数参数评估顺序[重复]

    这个问题在这里已经有答案了 在 C 和 C 中 函数参数的求值是否有固定的顺序 我的意思是 标准怎么说 是吗left to right or right to left 我从书中得到的信息令人困惑 是否有必要function call应该使
  • 带有自定义鉴别器的 EntityFramework Code First 继承

    我正在尝试在 EntityFramework Code First 中映射以下继承 public class Member public string ProjectName get set public string AssemblyNa
  • C# Julian 日期解析器

    我在电子表格中有一个单元格 它是 Excel 中的日期对象 但当它来自 C1 的 xls 类时 它会变成双精度型 类似于 2009 年 1 月 7 日的 39820 0 我读到这是儒略日期格式 有人可以告诉我如何在 C 中将其解析回 Dat
  • 为什么我不能对普通变量进行多态?

    我是一名Java程序员 最近开始学习C 我对某事感到困惑 据我了解 在 C 中 要实现多态行为 您必须使用指针或引用 例如 考虑一个类Shape与实施的方法getArea 它有几个子类 每个子类都以不同的方式重写 getArea 然后考虑以
  • 如何使用 xamarin 表单提示用户进行地理定位

    我正在 Xamarin Forms 应用程序中开发一个应用程序 需要请求地理位置权限 如果获得许可 它需要从设备获取地理位置数据 然后将地理位置坐标放入 Forecast io URL 我正在使用 James 的 Geolocator 插件
  • 字符串 c 的二叉树

    我正在尝试实现一个能够在 c 中保存字符串的二叉树 在让代码适用于整数之后 我尝试稍微修改它以处理字符数组 现在我似乎完全破解了代码 但不知道如何破解 任何帮助表示赞赏 include
  • 未找到 _sqlite3_open 等符号错误

    您好 我收到此错误 Undefined symbols sqlite3 open referenced from main in ccRlWVer o sqliite3 close referenced from main in ccRlW
  • OpenGL 计算着色器调用

    我有一个与新计算着色器相关的问题 我目前正在研究粒子系统 我将所有粒子存储在着色器存储缓冲区中 以便在计算着色器中访问它们 然后我派遣一个一维工作组 define WORK GROUP SIZE 128 shaderManager gt u
  • 在 C# WinForms 中预览文档(Word、Excel、PDF、文本文件等)?

    我正在开发一个 C WinForms 应用程序 我希望能够 预览 其中的各种文档类型 也就是说 当用户从列表中选择文件名时 它会在下面以相同的形式显示所选文件的预览 这很像 Outlook 允许您无需双击即可预览选定邮件的方式 有没有什么方
  • 清理堆分配对象的良好实践或约定?

    我正在学习C 我有 C C ObjC 背景 相当高级的语言 在 C 或 ObjC 上 作为函数或方法的结果返回堆分配的对象是很简单的 因为对象的清理是受管理的 按照惯例 会在适当的时候销毁 但我不知道在 C 中应该如何处理这个问题 例如 s
  • 如何从尖点库矩阵格式获取原始指针

    我需要从尖点库矩阵格式获取原始指针 例如 cusp coo matrix
  • 如何在没有 Visual Studio 的情况下将新文件添加到 .csproj 文件

    如何添加新文件到 csproj从命令提示符 我认为没有任何工具可以响应命令行上的 add project 命令来执行此操作 但我认为您可以幸运地创建一个程序 脚本来直接操作 csproj 文件的 XML 内容 csproj 文件的结构如下所

随机推荐