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:

  • Lowest
  • BelowNormal
  • Normal (default)
  • AboveNormal
  • Highest

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 (binary 0001): Core 0
  • 2 (binary 0010): Core 1
  • 3 (binary 0011): Cores 0 and 1
  • 4 (binary 0100): 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

  1. Use Thread.Priority when a task requires lower latency or must not be interrupted by background jobs.
  2. Use Native Affinity (SetThreadAffinityMask) for high-performance loops, low-latency trading engines, or real-time media processing where CPU cache misses hurt performance.
  3. Avoid Thread Pool Modification: Never alter affinity or priority on Task.Run or thread pool threads, as this alters shared resources used globally across the .NET runtime. Always create dedicated Thread instances when applying custom scheduling parameters.