.NET Framework 如何分配线程 ID?



我的问题与这个问题有关。该问题中的代码在循环中生成了多个线程,并且那里的 OP 观察到其日志记录中的线程 ID 似乎随着时间的推移而不断增加。这个问题是关于Java的,但它让我思考:JVM和.NET Framework首先如何分配线程ID?对于OP在他的帖子中描述的情况(用于验证线程是否确实按预期创建和销毁(,以及有关如何解释Visual Studio诊断工具日志的更多上下文,我对此特别感兴趣。我也对自己对框架如何工作的理解感兴趣。

我在这里主要问的是.NET Framework,因为同时问两者可能太宽泛了(尽管我也肯定会很高兴听到有关JVM的详细信息(。但是,下面是我在Visual Studio Diagnostic Tools的"事件"选项卡中获取的日志示例:

程序输出:线程0x44c已退出,代码为 0 (0x0(。

以下是按顺序排列的一些线程 ID:

0xcb0
0x2c4c
0x2c5c
0x1b10
0x1a60
0x27b4
0x2b80
0x2e04

这些似乎不是特别连续。该日志本身并不是非常有用的信息,例如,没有从Visual Studio中的"线程"窗口中获取更多上下文,因此我希望首先更多地了解如何分配这些日志将为这些事件提供更多上下文。

这是我正在使用的代码示例:

await jobs.AsyncForEach(async delegate (Job job)
{
// Do some stuff, some of which involves async/await HttpClient calls to a RESTful API
}, GlobalSettings.maxDegreeOfParallelism);

jobs的类型为List<Job>GlobalSettings.maxDegreeOfParallelism是指定最大并行度的const int(由于我们供应商的 API 限制(,AsyncForEachIEnumerable<T>上的扩展方法:

public static async Task AsyncForEach<T>(this IEnumerable<T> enumerable, Func<T, Task> action, int degreeOfParallelism)
{
List<Task> tasks = new List<Task>();
foreach (T item in enumerable)
{
if (tasks.Count >= degreeOfParallelism)
{
await Task.WhenAny(tasks);
tasks = tasks.Where(t => !t.IsCompleted).ToList();
}
Task actionTask = action(item);
tasks.Add(actionTask);
}
await Task.WhenAll(tasks);
}

这目前可以在以下三种环境之一中运行:WPF 应用程序、控制台应用程序或单元测试。我在此处显示的日志来自单元测试运行,但控制台应用程序日志看起来非常相似。

我确实意识到在这种情况下async/await的工作方式有所不同,并且如果没有同步上下文,则无法明确保证异步代码将在哪个线程上运行; 不过,对于它的价值,我没有使用new ThreadThreadPool.QueueUserWorkItem将任何代码显式分配给它自己的线程 或在此代码中的任何位置Task.Run

当我在 Google 上搜索此内容时,我看到了有关托管线程 ID 之间区别以及如何从线程获取 ID 的文档。但是,这些并没有真正回答.NET Framework最初是如何提出这些问题的。

我也很清楚 Visual Studio 线程窗口,它显示了线程的 ID、关联进程、托管 ID、类别、名称和位置。这也不能完全回答框架如何分配这些问题。

它没有。

有两种不同的实体:操作系统创建的本机线程和由 CLR 创建的托管线程。Thread ID 来自操作系统,ManagedThreadID 来自 CLR。两者都是计数器,在 CLR 启动时,通过操作系统具有更大的线程对象池。线程 ID 对于整个操作系统都是唯一的,CLR 线程在每个进程都是唯一的。

托管线程本质上是一种数据结构,存储在本机线程的 TLS 内存部分中,CLR 可以对其进行编辑,因此允许托管线程从一个本机线程切换到另一个本机线程,并通过纤程 API 在一个本机线程上托管多个托管线程。您可以使用Thread.BeginThreadAffinity将托管线程粘附到同一本机线程。此外,桌面应用程序必须将其主托管线程映射到同一本机线程(操作系统将重绘、键盘事件和多个其他内容的消息发送到特定本机线程的消息循环,CLR 拾取它(,因此这些应用程序中的主线程需要具有单线程单元模型,以确保它。

最新更新