Controlling actual Thread Priority in WinRT and Windows Phone
tl;dr: Setting the WorkItemPriority in ThreadPool.RunAsync actually changes the thread priority the code runs on, not just the position in the pending work queue.
It’s been a while since I’ve blogged, but this entry has been a finding that had long eluded me and this was a good chance to blog again. If you’re still reading, thanks :)
In the Plain Old (or might I say, complete) .NET framework, there was a pretty useful property named Thread.Priority, which gave a lot of control to app developers. This allowed a very control of what would run, where, and how.
Using this API, you could have CPU bound (hence blocking) code that could run at a very low priority, without the need to be yielded somehow, like it’s suggested now with async and Task.Yield().
I was under the impression, since Windows Phone 8.0 and WinRT 8.0 have been introduced, that there was no available way to control the actual thread priority, since either the property does not exist, or even Thread does not exist anymore.
The suggested counterpart, Task, does not provide such a feature, leaving developers no choice but chunking the work, by using clever tricks or work item priority scheduling.
A hidden gem
In WinRT 8.0+ and Windows Phone 8.0, there is one API, ThreadPool.RunAsync, which is very similar to .NET’s own thread pool, but provides an additional parameter, a WorkItemPriority.
Intuitively, and knowing historically how thread pools work, and that they are supposed to work with relatively small units of work, I thought “Ok, yet another API that will prioritize a work queue…”.
I could not be more wrong!
This API not only schedules in the queue using the work item priority, but also executes the work item with the specified thread priority !
To demonstrate the feature, here’s a simple code sample :
private void Profile()
{
ThreadPool
.RunAsync(_ => Run(s => textbox1.Text = s), WorkItemPriority.Low);
ThreadPool
.RunAsync(_ => Run(s => textbox2.Text = s), WorkItemPriority.Normal);
ThreadPool
.RunAsync(_ => Run(s => textbox3.Text = s), WorkItemPriority.High);
}
private void Run(Actiondisplay)
{
for (int i = 0; i < int.MaxValue; i++)
{
i.ToString(); // Slow things down a litthe bit...
if((i % 100000) == 0)
{
Dispatcher.BeginInvoke(() => display(i.ToString()));
}
}
}
The result is that after a few seconds, the higher the priority, the higher the number displayed on the screen.
Needless to say, this changes quite a bit of things, where it is definitely possible to having background threads that impact a lot less the dispatcher, even if they are 100% loaded. Good for the UI, good for the user.
That’s it for now, happy threading !