- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
在线程饥饿的场景中,我们首先要了解的就是线程是如何动态注入的?其实现如今的ThreadPool内部的实现逻辑非常复杂,而且随着版本的迭代内部逻辑也在不断的变化,有时候也没必要详细的去了解,只需在稍微宏观的角度去理解一下即可,我准备用三篇来详细的聊一聊线程注入的流程走向来作为线程饥饿的铺垫系列,这篇我们先从 Thread.Sleep 的角度观察线程的动态注入.
为了方便研究,我们用 Thread.Sleep 的方式阻塞线程池线程,然后观察线程的注入速度,参考代码如下:
static void Main(string[] args)
{
for (int i = 0; i < 10000; i++)
{
ThreadPool.QueueUserWorkItem((idx) =>
{
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} -> {idx}: 这是耗时任务");
Thread.Sleep(int.MaxValue);
}, i);
}
Console.ReadLine();
}
仔细观察卦中的输出,除了初始的12个线程喷涌而出,后面你会发现它的线程动态注入有时候大概是 500ms 一次,有时候会是 1000ms 一次,所以我们可以得到一个简单的结论:Thread.Sleep 场景下1s 大概会动态注入1~2个线程.
有了这个结论之后,接下来我们探究下它的底层逻辑在哪?
千言万语不及一张图,截图如下:
接下来我们来聊一下卦中的各个元素吧.
在 PortableThreadPool 中有一个 GateThread 类,专门掌管着线程的动态注入,默认情况下它大概是 500ms 被唤醒一次。这个是有很多逻辑源码支撑的.
private static class GateThread
{
public const uint GateActivitiesPeriodMs = 500;
private static void GateThreadStart()
{
while (true)
{
bool wasSignaledToWake = DelayEvent.WaitOne((int)delayHelper.GetNextDelay(currentTimeMs));
...
}
}
public uint GetNextDelay(int currentTimeMs)
{
uint elapsedMsSincePreviousGateActivities = (uint)(currentTimeMs - _previousGateActivitiesTimeMs);
uint nextDelayForGateActivities =
elapsedMsSincePreviousGateActivities < GateActivitiesPeriodMs
? GateActivitiesPeriodMs - elapsedMsSincePreviousGateActivities
: 1;
...
}
}
这个方法是用来判断任务最后一次出队的时间,即内部的lastDequeueTime 字段,这也是为什么有时候是1个周期(500ms),有时候是2个周期的底层原因,如果在一个周期内判断lastDequeueTime(490ms)<=500ms,那么在下一个周期内判断最后一次出队的时间自然就是 490ms+500ms,所以这就是为什么 Console 上显示大约 1s 的间隔的原因了,下面的代码演示了 lastDequeueTime 是如何存取的.
private static void GateThreadStart()
{
if (!disableStarvationDetection &&
threadPoolInstance._pendingBlockingAdjustment == PendingBlockingAdjustment.None &&
threadPoolInstance._separated.numRequestedWorkers > 0 &&
SufficientDelaySinceLastDequeue(threadPoolInstance))
{
bool addWorker = false;
if (addWorker)
{
WorkerThread.MaybeAddWorkingWorker(threadPoolInstance);
}
}
}
private static bool SufficientDelaySinceLastDequeue(PortableThreadPool threadPoolInstance)
{
uint delay = (uint)(Environment.TickCount - threadPoolInstance._separated.lastDequeueTime);
uint minimumDelay;
if (threadPoolInstance._cpuUtilization < CpuUtilizationLow)
{
minimumDelay = GateActivitiesPeriodMs;
}
else
{
minimumDelay = (uint)threadPoolInstance._separated.counts.NumThreadsGoal * DequeueDelayThresholdMs;
}
return delay > minimumDelay;
}
private static void WorkerDoWork(PortableThreadPool threadPoolInstance, ref bool spinWait)
{
bool alreadyRemovedWorkingWorker = false;
while (TakeActiveRequest(threadPoolInstance))
{
threadPoolInstance._separated.lastDequeueTime = Environment.TickCount;
if (!ThreadPoolWorkQueue.Dispatch())
{
}
}
}
这个方法是用来创建线程的主体逻辑,在线程池中由上层的 MaybeAddWorkingWorker 调用,参考如下:
internal static void MaybeAddWorkingWorker(PortableThreadPool threadPoolInstance)
{
while (toCreate > 0)
{
CreateWorkerThread();
toCreate--;
}
}
private static void CreateWorkerThread()
{
Thread workerThread = new Thread(s_workerThreadStart);
workerThread.IsThreadPoolThread = true;
workerThread.IsBackground = true;
workerThread.UnsafeStart();
}
这里有一个注意点:上面的 while (toCreate > 0) 代码预示着一个周期内(500ms)可能会连续创建多个工作线程,但在饥饿的大多数情况下都是toCreate=1的情况.
说了这么多,能不能用一些手段让我眼见为实呢?要想眼见为实也不难,可以用 dnspy 断点日志功能观察即可,分别在如下三个方法上下断点.
在此处下断点的目的用于观察 GateThread 的唤醒周期时间,截图如下:
这里下断点主要是观察当前的延迟如果超过 500ms 时是否真的会通过 CreateWorkerThread 创建线程。截图如下:
最后我们在 MaybeAddWorkingWorker 方法的底层的线程创建方法 CreateWorkerThread 中下一个断点.
所有的埋点下好之后,我们让程序跑起来,观察 output 窗口的输出.
从输出窗口中可以清晰的看到以500ms为界限判断啥时该创建,啥时不该创建.
可能有些朋友很感慨,线程的动态注入咋怎么慢?1s才1-2个,难怪会出现线程饥饿。。。哈哈,下一篇我们聊一聊Task.Result下的注入优化.
最后此篇关于聊一聊C#线程池的线程动态注入(上)的文章就讲到这里了,如果你想了解更多关于聊一聊C#线程池的线程动态注入(上)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
在 JavaScript 中,我们可以动态创建 元素并附加到 部分,以便为大量元素应用 CSS 规则。 这种方法的优点或缺点是什么? 如果它确实提供了与元素上的 javascript 迭代相比的性
我有这个代码 import "./HTTPMethod.dart"; import '../../DataModel/DataModel.dart'; mixin RouterMixin { HT
哪些 OLAP 工具支持动态、动态地创建维度或层次结构? 例如,层次结构将成员定义为:“前 5 名”、“前 6-10 名”、“其他”... 计算成员是通常的答案,我正在寻找不同的东西。计算器的问题。成
我正在 CakePHP 中创建一个“表单编辑器”。 该界面允许用户选择要应用于字段的验证,例如数字、电子邮件等 因此,我需要根据用户输入为模型动态创建验证。为此,我可以使用验证对象:https://b
这是一个场景: 我有一个Web服务,我们将其称为部署在tomcat(轴)上的StockQuoteService。通过此 Web 服务公开了 getStockQuote() 方法。 现在,我想构建一个
我正在尝试从服务器获取 JSON 响应并将其输出到控制台。 Future login() async { var response = await http.get( Uri.
我从另一个问题中得到了这段代码(感谢 chunhunghan)。我需要创建一个登录屏幕,并尝试根据服务器发回给我的响应来验证用户凭据,但是每次我尝试运行代码时,它都会给我“未处理的异常:Interna
当我在“Dart”主程序中运行它时,一切正常,并且我得到了一个与会者列表。但是,当我在我的 Flutter 应用程序中调用它时,出现错误: flutter:“List”类型不是“List>”类型的子类
本文实例为大家分享了js实现验证码动态干扰的具体代码,供大家参考,具体内容如下 效果一 效果二 代码一 ?
目前我正在为我的网站使用 No-Ip,我想使用 cloudflare 来抵御 ddos 和机器人程序。我注意到您需要一个用于 cloudflare 的域。我还搜索了网络,发现了一个叫做 cloud
有没有办法在 Excel VBA 中构建动态 if 语句?基本上我正在尝试创建一个参数化计算,用户将能够输入不同的变量,即 变量 1 “变量 2” “变量 3” 在这种情况下 变量 1 是单元格引用
大家好, 请查看上面的图片,我有两张 table 。在下面代码的第一个表中,我得到了这种格式。 但我想像 Table2 那样格式化,每个合并单元格中的行数是动态的,而且不一样。 有没有办法像table
如何根据我添加的 View 修改标题部分的高度?heightForHeaderInSection在 viewForHeaderInSection 之前被调用我不知道 View 大小,直到我创建它。 最
是否存在在运行时生成 AST/解析树的解析器?有点像一个库,它会接受一串 EBNF 语法或类似的东西并吐出数据结构? 我知道 antlr、jlex 和他们的同类。他们生成可以做到这一点的源代码。 (喜
我在持有汽车制造商的表格上有一个 MultipleChoiceField。我想将我的汽车数据库过滤到已检查的品牌,但这会导致问题。如何动态获取所有 Q(make=...) 语句? 我如何开始:['va
$end = preg_replace($pattern, $replacement, $str); 如何使替换字符串 $replacement 随 $str 中的每次匹配而变化?例如,我想用关联的图
我正在编写一个 VBA 程序,用于过滤表中的值。我试图使其成为一个适用于您提供的所有表格的通用程序。在我的程序中,我必须设置它正在过滤的表的范围:Set rng = dataSheet.Range("
我正在循环一个元素数组,并且我想使用给定的模板递归地显示该元素 然后在该模板内使用带有切换功能的按钮来显示/隐藏给定元素的Child的更深级别模板(Child也是一个元素) 这是我的模板
从客户端(html)发送表单,服务器端通过选择选项之一决定运行哪个函数。 const decideWho = (form) => { const choice = form.choice; c
我有一个具有以下属性的按钮: circle_normal.xml(在 res/drawable 中) circle.xml(在 res/drawable 中)
我是一名优秀的程序员,十分优秀!