
Using the SpinWait construct
This recipe will describe how to wait on a thread without involving kernel-mode constructs. In addition, we introduce SpinWait
, a hybrid synchronization construct designed to wait in the user mode for some time, and then switch to the kernel mode to save CPU time.
Getting ready
To step through this recipe, you will need Visual Studio 2015. There are no other prerequisites. The source code for this recipe can be found at BookSamples\Chapter2\Recipe9
.
How to do it...
To understand how to wait on a thread without involving kernel-mode constructs, perform the following steps:
- Start Visual Studio 2015. Create a new C# console application project.
- In the
Program.cs
file, add the followingusing
directives:using System; using System.Threading; using static System.Console; using static System.Threading.Thread;
- Below the
Main
method, add the following code:static volatile bool _isCompleted = false; static void UserModeWait() { while (!_isCompleted) { Write("."); } WriteLine(); WriteLine("Waiting is complete"); } static void HybridSpinWait() { var w = new SpinWait(); while (!_isCompleted) { w.SpinOnce(); WriteLine(w.NextSpinWillYield); } WriteLine("Waiting is complete"); }
- Inside the
Main
method, add the following code:var t1 = new Thread(UserModeWait); var t2 = new Thread(HybridSpinWait); WriteLine("Running user mode waiting"); t1.Start(); Sleep(20); _isCompleted = true; Sleep(TimeSpan.FromSeconds(1)); _isCompleted = false; WriteLine("Running hybrid SpinWait construct waiting"); t2.Start(); Sleep(5); _isCompleted = true;
- Run the program.
How it works...
When the main program starts, it defines a thread that will execute an endless loop for 20 milliseconds until the main thread sets the _isCompleted
variable to true
. We could experiment and run this cycle for 20-30 seconds instead, measuring the CPU load with the Windows task manager. It will show a significant amount of processor time, depending on how many cores the CPU has.
We use the volatile
keyword to declare the _isCompleted
static field. The volatile
keyword indicates that a field might be modified by multiple threads being executed at the same time. Fields that are declared volatile
are not subject to compiler and processor optimizations that assume access by a single thread. This ensures that the most up-to-date value is present in the field at all times.
Then, we use a SpinWait
version, which on each iteration prints a special flag that shows us whether a thread is going to switch to a blocked
state. We run this thread for 5 milliseconds to see that. In the beginning, SpinWait
tries to stay in the user mode, and after about nine iterations, it begins to switch the thread to a blocked state. If we try to measure the CPU load with this version, we will not see any CPU usage in the Windows task manager.