La séparation entre la gestion et les fils du système d'exploitation remonte à .Net 2.0, et les plans de l'équipe SQL Server pour mettre en œuvre des fils .Net en utilisant des fibres. Cela ne s'est jamais réellement passé, alors même s'il n'y a aucune garantie qu'un thread géré fonctionnera toujours sur le même thread de système d'exploitation, en pratique, c'est toujours le cas pour tous les hôtes .Net actuels. Étant donné que cela n'a pas changé dans toutes les années depuis l'introduction de .Net 2.0, il est peu probable que cela change jamais.
Il est possible de renforcer notre confiance, même pour les futures versions de .Net en utilisant la méthode System.Threading.Thread.BeginThreadAffinity
. Cela garantit que le thread géré restera sur le même thread de système d'exploitation (donc il ne fait rien sur l'hôte CLR par défaut, car cela est déjà vrai par défaut). Je suppose qu'il est toujours possible que d'autres threads gérés puissent partager le même thread de système d'exploitation, mais cela semble improbable et n'est certainement pas le cas dans tous les hôtes .Net actuels. .Net permet d'accéder aux threads du système d'exploitation natif à l'aide de la classe System.Diagnostics.ProcessThread
. Cette classe permet de modifier l'affinité du processeur du thread à l'aide de la propriété ProcessorAffinity
. Cependant, relier un thread managé particulier à son ProcessThread
a été délibérément difficile.
La seule façon de le faire est à l'intérieur du fil lui-même. Utilisez la méthode System.AppDomain.GetCurrentThreadId
(ou PInvoke la fonction GetCurrentThreadId
si vous ne voulez pas appeler une méthode désapprouvée, bien que cela ne fonctionne pas avec Mono sur les systèmes d'exploitation autres que Windows). Cela peut ensuite correspondre à la propriété ProcessThread.Id
.
Cela permet de définir l'affinité du processeur avec le code suivant du fil (être appelé à l'intérieur du fil):
/// <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);
}
Gardez à l'esprit que, si cela fonctionne sur les versions actuelles de .Net, théoriquement l'absence de garantie que les threads gérés sont liés aux threads du système d'exploitation pourrait casser ce code dans le futur. Cependant, je considère cela extrêmement improbable.