是我们可以在.NET中安全完成的核心亲缘关系吗?

spa*_*man 4 .net c# multithreading affinity cpu-cores

我在StackOverflow中查看了有关
在.NET中实现线程的核心亲和力的信息。

一些答案说.NET不支持它自己的(托管)线程,
而仅支持操作系统上运行的非托管线程。

另一方面,在其他答案中,请提及以下属性:
- ProcessThread.IdealProcessor 链接
- ProcessThread.ProcessorAffinity 链接

可以看出,这两个属性不是Thread类的属性,而是ProcessThread类的属性。

因此,我想问一问:
如果有人正在创建.NET应用程序,
并且想为其应用程序的线程设置Core亲缘关系,那么
在.NET托管线程上这样做是否安全且受支持?

(如果是,那么我想知道为什么这两个属性在ProcessThread类上公开
而不在Thread类上公开?)

PS:我使用的是.NET Framework v3.5和v2.0,
而不是框架的较新版本。

Lua*_*aan 5

不。

.NET 线程不会 1:1 映射到操作系统线程。线程亲缘关系用于操作系统线程。由于运行时可以随意在 OS 线程之间切换 .NET 线程,因此设置处理器关联充其量无济于事,并且在典型的多线程场景中会浪费性能。

请注意,它ProcessThread仅包含有关在进程中运行操作系统线程的信息。当您询问进程具有哪些线程时,这就是您所得到的 - 它是操作系统的一部分,而不是 .NET。相比之下,Thread是关于你的线程,只有托管线程。


Had*_*ais 5

这是一个更详细的答案,某些人可能会觉得有用。

如果您想专门使用现有.NET Standards的API进行此操作,则答案的确是“否”,我将解释原因。我还将讨论如何使用一组.NET Standard 2.0 API和单个OS API或单个.NET Core 2.1 API来实现这一目标。

的确,在托管应用程序的整个生命周期中,通常都不能保证托管线程和本机线程之间固定的一对一映射。这基于CLI标准I.12.3.1:

CLI管理多个并发控制线程(不一定与主机操作系统提供的线程相同),多个托管堆和共享内存地址空间。

尚未明确说明的一件事是,用于运行托管线程的本机线程集合是全部包含在同一进程中还是包含在多个进程中。但是CLI标准和.NET文档中有声明表明整个托管应用程序都在单个OS进程中。另外,我还不知道在多个OS进程上安排托管线程的任何实现。

让我们首先考虑一个简单的情况,其中只有一个托管线程和/或只有一个本机线程。通过使用Process.ProcessorAffinity属性设置整个流程的亲和力,可以轻松处理这种情况。无论将单个托管线程映射到多个本机线程还是将单个本地线程映射到多个托管线程,整个托管应用程序只能有一个亲和力值。

否则,可能存在多个关联。类型的本机线程ProcessThread提供只写属性ProcessorAffinity。托管线程类型不Thread提供此类API。但是,它提供了BeginThreadAffinity静态方法,该方法允许当前托管线程将其映射固定到其当前映射到的任何本机线程,直到托管线程调用为止EndThreadAffinity。注意,这BeginThreadAffinity并不是对运行时的提示。要么引发异常,要么使用当前托管线程的固定映射成功返回异常。现在,如果我们可以获取当前的本机线程,则可以使用ProcessThread.ProcessorAffinity。不幸的是,与获取当前托管线程相反,没有标准托管API返回当前本机线程的引用或标识符。如您所见,我们只能使用.NET Standard 2.0 API来实现固定的映射,但是无法找出哪个本地线程映射到了哪个托管线程。我不认为没有这样的API有充分的技术理由。

一种进行方式是调用某些与OS相关的API,以获取当前本机线程的ID。在Windows上,GetCurrentThreadId来自kernel32.dll。当前的托管线程可以调用此API来获取当前本机线程的ID。然后,ProcessThread通过使用Process.GetCurrentProcess().Threads并找到具有匹配ID 的本地线程来迭代本地线程,可以获得对相应对象的引用。之后,ProcessThread.ProcessorAffinity可以用来有效地设置当前托管线程的亲和力。请注意,由于这ProcessThread.ProcessorAffinity是一个只写属性,因此没有.NET Standard 2.0 API使您能够还原旧的亲和力。我不知道为什么它是只写的。

另一种更为复杂的方法是使用Thread.GetCurrentProcessorId,当前仅存在于.NET Core 2.1(最新版本)中。您可以设置每个本机线程与特定处理器的关联性,并检查该处理器上当前正在运行哪个托管线程。最终,您可以确定哪个托管线程映射到哪个本机线程。

尽管可以实现上面讨论的固定映射,但是在我看来,BeginThreadAffinity这不能保证每个托管线程都映射到唯一的本机线程。因此,无法以符合标准的方式实现一对一映射。

在.NET Core中,ProcessThread.ProcessorAffinity仅在Windows上实现。在其他操作系统上,它抛出PlatformNotSupportedException