The Windows Vista kernel added support for I/O and memory priorities. These allow background work (such as search indexing or virus scanning) to reduce its impact on foreground applications beyond what is possible simply by using a low thread CPU priority. According to the SetThreadPriority documentation, “For threads that perform background work such as file I/O, network I/O, or data processing, it is not sufficient to adjust the CPU scheduling priority; even an idle CPU priority thread can easily interfere with system responsiveness when it uses the disk and memory.”
Applications can opt in to low I/O and memory priority by passing new flags to
SetThreadPriority
(THREAD_MODE_BACKGROUND_BEGIN
and
THREAD_MODE_BACKGROUND_END
) or SetPriorityClass
(PROCESS_MODE_BACKGROUND_BEGIN
and PROCESS_MODE_BACKGROUND_END
).
These new priority levels aren’t exposed through the .NET Framework, but can be accessed by using P/Invoke. First, declare the constants and functions from the Windows API:
Second, write a C# wrapper for those functions. I use Thread.BeginThreadAffinity to notify the runtime (strictly speaking, the CLR host) that the code that’s being executed depends on the identity of the underlying OS thread. The return type, Scope, has been covered already on this blog.
Third, use the wrapper like so:
Note that this only has effect on Windows Vista, Windows Server 2008, and later; you’d also want to lower Thread.Priority during the background work if your application runs on earlier operating systems.
And while this does appear to work quite nicely in testing, it could actually be dangerous in production code. It’s possible that this could have unpredictable and hazardous interactions with the garbage collector, the finalizer thread, or other components of the .NET Runtime. Furthermore, if a CLR host ever multiplexes many managed threads to one OS thread, changing the priority of the OS thread would be too heavy-handed. Perhaps a future version of the framework will expose background processing mode to managed threads in a safe way; until then it’s probably best to consider advanced native threading features (background mode, fibers, CPU affinity, etc.) to be off limits to managed code.
Posted by Bradley Grainger on October 06, 2008