我的问题与
this question
。这个问题中的代码在一个循环中生成了多个线程,那里的操作人员观察到日志中的线程ID似乎随着时间的推移而不断增加。这个问题是关于Java的,但它让我思考:JVM和.NETFramework如何首先分配线程ID?我对这一点特别感兴趣,因为OP在他的文章中描述了这样的情况(用于验证线程是否按预期实际创建和销毁),以及有关如何解释Visual Studio诊断工具日志的更多上下文。我也对我自己对框架如何工作的理解感兴趣。
我在这里主要问的是.NET框架,因为它可能太宽泛了,不能同时问这两个问题(尽管我肯定很高兴听到有关JVM的详细信息)。但是,下面是我在Visual Studio诊断工具的事件选项卡中获取的日志示例:
程序输出
:线程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节流阀),以及
AsyncForEach
是上的扩展方法
IEnumerable<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 Thread
,
ThreadPool.QueueUserWorkItem
或
Task.Run
在这段代码的任何地方。
当我在谷歌上搜索这个时,我看到了关于托管线程ID之间的区别以及如何从线程获取ID的文档。然而,这些并不能真正回答一个问题,即.NET框架是如何首先提出这些问题的。
我还很清楚Visual Studio线程窗口,它显示线程的ID、关联进程、托管ID、类别、名称和位置。这也不能确切地回答框架如何分配这些任务的问题。