How can I set processor affinity to a thread or a Task in .NET?
Asked Answered
S

6

33

Can we set two threads or two tasks to execute with different processor affinity in a C# application?

I have read about SetThreadAffinityMask, but have found no example of how that should be used.

Alternatively, is there any way for TPL (Task Parallel Library) to execute two threads/Tasks with high priority to use 100% CPU?

Superman answered 24/3, 2010 at 18:57 Comment(0)
U
51

Process and ProcessThread objects have a ProcessorAffinity property of IntPtr type that can be directly manipulated to read/change affinity for up to 64 processors:

using System.Diagnostics;
Process proc = Process.GetCurrentProcess();
long affinityMask = 0x000F; // use only any of the first 4 available processors
proc.ProcessorAffinity = (IntPtr)affinityMask;
 
ProcessThread thread = proc.Threads[0];
affinityMask = 0x0002; // use only the second processor, despite availability
thread.ProcessorAffinity = (IntPtr)affinityMask;

You can also use the thread's IdealProcessor property to allow the scheduler to prefer running the thread on a specified processor (without guarantee).

Yes, it's that easy :)

Reference: ProcessThread.ProcessorAffinity Property

United answered 18/11, 2010 at 21:48 Comment(5)
Q: "Can we set two thread or two task to execute with different processor affinity in C# Application?" Notice that the OP asks for different threads in the same process. So the question is not about process, but thread affinity.Skycap
There is Thread.SetProcessorAffinity() but it only applies to XNA for the Xbox 360. There is no OOTB solution in vanilla .NET, as far as I know.Skycap
@andras: Good point, I guess I missed the mark a bit. There is a way to do this for threads too, and I edited my example accordingly. Thanks!United
You are welcome. ;) It could be made even more useful if we could get the ProcessThread of the current Thread so that we could use this on the current thread and then get a nice using() construct to revert to the original affinity at the end of the block. That would be great. ;)Skycap
also i've found this code in internet that probably can be used code.google.com/p/disruptor-net/source/browse/trunk/Source/…Suspensive
S
6

Actually, .NET Framework and Windows manage the threads pretty well, distributing them evenly on every processor. However, the distribution of threads can be manipulated manually using Process and ProcessThread.

using System;
using System.Diagnostics;
using System.Threading;

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //Get the our application's process.
            Process process = Process.GetCurrentProcess();

            //Get the processor count of our machine.
            int cpuCount = Environment.ProcessorCount;
            Console.WriteLine("CPU Count : {0}", cpuCount);

            //Since the application starts with a few threads, we have to
            //record the offset.
            int offset = process.Threads.Count;
            Thread[] threads = new Thread[cpuCount];
            Console.WriteLine(process.Threads.Count);
            LogThreadIds(process);

            //Create and start a number of threads that equals to
            //our processor count.
            for (int i = 0; i < cpuCount; ++i)
            {
                Thread t = new Thread(new ThreadStart(Calculation))
                { IsBackground = true };
                t.Start();
            }

            //Refresh the process information in order to get the newest
            //thread list.
            process.Refresh();
            Console.WriteLine(process.Threads.Count);
            LogThreadIds(process);

            //Set the affinity of newly created threads.
            for (int i = 0; i < cpuCount; ++i)
            {
                //process.Threads[i + offset].ProcessorAffinity = (IntPtr)(1L << i);
                //The code above distributes threads evenly on all processors.
                //But now we are making a test, so let's bind all the threads to the
                //second processor.
                process.Threads[i + offset].ProcessorAffinity = (IntPtr)(1L << 1);
            }
            Console.ReadLine();
        }
        static void Calculation()
        {
            //some extreme loads.
            while (true)
            {
                Random rand = new Random();
                double a = rand.NextDouble();
                a = Math.Sin(Math.Sin(a));
            }
        }
        static void LogThreadIds(Process proc)
        {
            //This will log out all the thread id binded to the process.
            //It is used to test whether newly added threads are the latest elements
            //in the collection.
            Console.WriteLine("===Thread Ids===");
            for (int i = 0; i < proc.Threads.Count; ++i)
            {
                Console.WriteLine(proc.Threads[i].Id);
            }
            Console.WriteLine("===End of Thread Ids===");
        }
    }
}

Now check the task manager, we can see that that the second processor is taking all the work loads. The task manager window

Score answered 14/1, 2018 at 3:26 Comment(3)
example is wrong, process.Threads[i + offset].ProcessorAffinity = (IntPtr)(1L << 1); sets the processor affinity to processor 1 onlyRude
@FabioAngela But now we are making a test, so let's bind all the threads to the second processor. I don't see how my example contradicts the description.Score
my bad, commented code above reports i and not 1 so it's fine (read both as 1 at first)Rude
E
3
Process.GetCurrentProcess().ProcessorAffinity = (System.IntPtr)2; 
Earthly answered 10/1, 2019 at 7:23 Comment(2)
Although this is answer to different question, it worked for me. Thanks.Courageous
This will change the affinity of whole application (process) effectively assigning same affinity to all threads of this app. OP asked about changing affinity of 2 threads within same app.Myo
F
2

Actually OS is capable of load balancing your cores/processors but if you want to do it explicitly use mentioned via PInvoke. You pass id of thread (not managed one!) and mask - the bit array of cores.

Fungicide answered 24/3, 2010 at 19:5 Comment(1)
how do you test if it actually works?Pandect
A
0

The following example from MSDN shows how to set the ProcessorAffinity property for an instance of Notepad to the first processor.

using System;
using System.Diagnostics;

namespace ProcessThreadIdealProcessor
{
    class Program
    {
     static void Main(string[] args)
        {
        // Make sure there is an instance of notepad running.
        Process[] notepads = Process.GetProcessesByName("notepad");
        if (notepads.Length == 0)
            Process.Start("notepad");
            ProcessThreadCollection threads;
            //Process[] notepads; 
            // Retrieve the Notepad processes.
            notepads = Process.GetProcessesByName("Notepad");
            // Get the ProcessThread collection for the first instance
            threads = notepads[0].Threads;
            // Set the properties on the first ProcessThread in the collection
            threads[0].IdealProcessor = 0;
            threads[0].ProcessorAffinity = (IntPtr)1;
        }
    }
}
Alliance answered 3/5, 2014 at 18:9 Comment(1)
Copy and pasted from msdn.microsoft.com/en-us/library/…. Source should be referenced...Especial
R
-1

I want to share this code, showing how to do it with TPL.

With threads,

        using System;
        using System.Diagnostics;
        using System.Threading;
        
        static void Fibonacci(int n)
        {
            int a = 0, b = 1, c = 0;
            for (int i = 0; i < n; i++)
            {
                c = a + b;
                a = b;
                b = c;
            }
            Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId}: Fibonacci({n}) = {c}");
        }
        
        int processorCount = Environment.ProcessorCount;
        
        for (int i = 0; i < processorCount; i++)
        {
            int processorNum = i;
            Thread thread = new Thread(() => Fibonacci(processorNum * 10));
            
            // Set processor affinity for each thread
            thread.ProcessorAffinity = (IntPtr)(1 << processorNum);
            
            thread.Start();
        }

With TPL (Below code will not work, just a failed attempt),

        using System;
        using System.Diagnostics;
        using System.Threading.Tasks;
        
        int processorCount = Environment.ProcessorCount;
        
        Task[] tasks = new Task[processorCount];
        for (int i = 0; i < processorCount; i++)
        {
            int processorNum = i;
            tasks[i] = Task.Factory.StartNew(() => Fibonacci(processorNum * 10), TaskCreationOptions.LongRunning);
        }

        // Set processor affinity for each task
        ProcessThreadCollection currentThreads = Process.GetCurrentProcess().Threads;
        for (int i = 0; i < processorCount; i++)
        {
            ProcessThread assignedThread = null;
            foreach (ProcessThread thread in currentThreads)
            {
                # Problem here: Is possible to find out TPL task's thread ID, after it started? And then assign processor affinity after startNew?
                if (thread.Id == tasks[i].Id)
                {
                    assignedThread = thread;
                    break;
                }
            }
            
            if (assignedThread != null)
            {
                int processorNum = i;
                assignedThread.ProcessorAffinity = (IntPtr)(1 << processorNum);
            }
        }

        Task.WaitAll(tasks);

        Console.ReadLine();
Rossiter answered 13/3 at 3:3 Comment(3)
I am very suspicious about the "with TPL" section. Specifically the condition if (thread.Id == tasks[i].Id) looks like comparing for equality apples to oranges. My suggestion is to remove it, and keep just the "with threads" section.Uvular
I think you're correct. You think it's possible to find out TPL task's thread ID, after it started? And then assign processor affinity after startNew?Rossiter
One idea is to configure the current thread internally, using the Thread.CurrentThread property. Another is to write a custom TaskScheduler that has a ProcessorAffinity property, which then runs its tasks on dedicated threads configured accordingly. As a starting point see this answer.Uvular

© 2022 - 2024 — McMap. All rights reserved.