(发布这个答案是因为我在任何地方都找不到关于如何做到这一点的完整解释,所以我认为它可能对某人有一些价值)
如何在 Microsoft .Net 中设置特定线程的处理器亲和性?通过 设置进程的亲和性是微不足道的System.Diagnostics.Process.ProcessorAffinity
,但是System.Threading.Thread
该类不提供此类功能,并且.Net 不保证托管线程链接到任何特定的操作系统线程。
(发布这个答案是因为我在任何地方都找不到关于如何做到这一点的完整解释,所以我认为它可能对某人有一些价值)
如何在 Microsoft .Net 中设置特定线程的处理器亲和性?通过 设置进程的亲和性是微不足道的System.Diagnostics.Process.ProcessorAffinity
,但是System.Threading.Thread
该类不提供此类功能,并且.Net 不保证托管线程链接到任何特定的操作系统线程。
托管线程和操作系统线程之间的分离可以追溯到 .Net 2.0,并且 SQL Server 团队计划使用纤程实现 .Net 线程。这从未真正发生过,因此虽然不能保证托管线程将始终在同一个操作系统线程上运行,但实际上对于所有当前的 .Net 主机都是如此。鉴于自 .Net 2.0 推出以来的这些年里,这种情况一直没有改变,这种情况不太可能永远改变。
System.Threading.Thread.BeginThreadAffinity
通过使用该方法,即使对于 .Net 的未来版本,也可以增强我们的信心。这保证了托管线程将保持在同一个操作系统线程上(因此它在默认 CLR 主机上什么也不做,因为默认情况下已经如此)。我想其他托管线程仍然有可能共享相同的操作系统线程,但这似乎不太可能,并且在任何当前的 .Net 主机中绝对不是这种情况。
.Net 提供了使用该类访问本机操作系统线程System.Diagnostics.ProcessThread
的能力,并且该类具有使用该ProcessorAffinity
属性更改线程的处理器亲和性的能力。然而,将一个特定的托管线程链接到它ProcessThread
是故意困难的。
唯一真正的方法是从线程本身内部。使用该System.AppDomain.GetCurrentThreadId
方法(GetCurrentThreadId
如果您不想调用已弃用的方法,则使用 PInvoke 函数,尽管这不适用于 Windows 以外的操作系统上的 Mono)。然后可以将其与ProcessThread.Id
属性相匹配。
这使得可以使用以下代码设置线程的处理器亲和性(从线程内部调用):
/// <summary>
/// Sets the processor affinity of the current thread.
/// </summary>
/// <param name="cpus">A list of CPU numbers. The values should be
/// between 0 and <see cref="Environment.ProcessorCount"/>.</param>
public static void SetThreadProcessorAffinity(params int[] cpus)
{
if( cpus == null )
throw new ArgumentNullException("cpus");
if( cpus.Length == 0 )
throw new ArgumentException("You must specify at least one CPU.", "cpus");
// Supports up to 64 processors
long cpuMask = 0;
foreach( int cpu in cpus )
{
if( cpu < 0 || cpu >= Environment.ProcessorCount )
throw new ArgumentException("Invalid CPU number.");
cpuMask |= 1L << cpu;
}
// Ensure managed thread is linked to OS thread; does nothing on default host in current .Net versions
Thread.BeginThreadAffinity();
#pragma warning disable 618
// The call to BeginThreadAffinity guarantees stable results for GetCurrentThreadId,
// so we ignore the obsolete warning
int osThreadId = AppDomain.GetCurrentThreadId();
#pragma warning restore 618
// Find the ProcessThread for this thread.
ProcessThread thread = Process.GetCurrentProcess().Threads.Cast<ProcessThread>()
.Where(t => t.Id == osThreadId).Single();
// Set the thread's processor affinity
thread.ProcessorAffinity = new IntPtr(cpuMask);
}
请记住,虽然这适用于当前版本的 .Net,但理论上缺乏托管线程绑定到 OS 线程的保证可能会在将来破坏此代码。但是,我认为这极不可能。