How to Set Thread Priority and Affinity in C
Optimizing high-performance C# applications often requires fine-grained control over execution resources. Thread priority dictates how the operating system's scheduler balances CPU time among competing threads, while thread affinity binds a specific thread to designated CPU cores to improve cache locality and minimize context switching. This guide explains how to configure both thread priority and processor affinity in C# using managed APIs and native Windows interop.
Setting Thread Priority
The .NET runtime provides direct support for managing thread
scheduling precedence through the System.Threading.Thread
class. Setting priority instructs the OS scheduler to give a thread more
or less time slice execution relative to other threads of the same
process.
Managed Priority Levels
You configure the priority by setting the
Thread.Priority property to one of the
ThreadPriority enum values:
LowestBelowNormalNormal(default)AboveNormalHighest
Code Example
using System;
using System.Threading;
class PriorityExample
{
static void Main()
{
Thread worker = new Thread(PerformWork);
// Elevate priority before starting the thread
worker.Priority = ThreadPriority.AboveNormal;
worker.Start();
worker.Join();
}
static void PerformWork()
{
// Can also be set within the thread itself
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Console.WriteLine($"Current Thread Priority: {Thread.CurrentThread.Priority}");
}
}Note: Use ThreadPriority.Highest sparingly.
Overusing high priority can starve background runtime threads (such as
the Garbage Collector) and operating system tasks.
Setting Thread Affinity
Processor affinity restricts a thread to execute only on specific CPU
cores. Unlike process-level affinity, which can be configured via
Process.GetCurrentProcess().ProcessorAffinity, individual
thread affinity in Windows requires interacting with native OS
threads.
The standard way to set native thread affinity in C# is by invoking
the Windows API function SetThreadAffinityMask.
Using P/Invoke with
SetThreadAffinityMask
A bitmask represents the assigned processor cores. For example:
1(binary0001): Core 02(binary0010): Core 13(binary0011): Cores 0 and 14(binary0100): Core 2
Code Example
using System;
using System.Runtime.InteropServices;
using System.Threading;
class AffinityExample
{
[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentThread();
[DllImport("kernel32.dll")]
private static extern UIntPtr SetThreadAffinityMask(IntPtr hThread, UIntPtr dwThreadAffinityMask);
static void Main()
{
Thread dedicatedThread = new Thread(RunPinnedWork);
dedicatedThread.Start();
dedicatedThread.Join();
}
static void RunPinnedWork()
{
// Pin this thread to CPU Core 2 (bitmask: 1 << 2 = 4)
ulong affinityMask = 1UL << 2;
IntPtr threadHandle = GetCurrentThread();
UIntPtr previousMask = SetThreadAffinityMask(threadHandle, new UIntPtr(affinityMask));
if (previousMask == UIntPtr.Zero)
{
Console.WriteLine("Failed to set thread affinity.");
}
else
{
Console.WriteLine("Successfully pinned thread to CPU Core 2.");
}
// Execute performance-critical workload
}
}Setting Affinity via
ProcessThread
Alternatively, you can assign affinity without raw P/Invoke by
locating the current thread within
Process.GetCurrentProcess().Threads.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
class ProcessThreadAffinity
{
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
public static void SetCurrentThreadAffinity(int coreIndex)
{
int osThreadId = GetCurrentThreadId();
ProcessThreadCollection threads = Process.GetCurrentProcess().Threads;
foreach (ProcessThread thread in threads)
{
if (thread.Id == osThreadId)
{
// Assign to specific core (e.g., Core 0 = 0x01, Core 1 = 0x02)
thread.ProcessorAffinity = (IntPtr)(1 << coreIndex);
break;
}
}
}
}Summary Checklist
- Use
Thread.Prioritywhen a task requires lower latency or must not be interrupted by background jobs. - Use Native Affinity
(
SetThreadAffinityMask) for high-performance loops, low-latency trading engines, or real-time media processing where CPU cache misses hurt performance. - Avoid Thread Pool Modification: Never alter
affinity or priority on
Task.Runor thread pool threads, as this alters shared resources used globally across the .NET runtime. Always create dedicatedThreadinstances when applying custom scheduling parameters.