Home
C#
Interlocked Examples
This page was last reviewed on Aug 20, 2021.
Dot Net Perls
Interlocked. This C# class helps with threading. It safely changes the value of a shared variable from multiple threads. It is part of System.Threading.
C# lock info. Using threads safely is possible with the lock statement. But you can instead use the Interlocked type for simpler and faster code.
lock
Interlocked.Add. First, forget about the addition, subtraction and assignment operators. Instead, you will use the Add, Increment, Decrement, Exchange and CompareExchange methods.
Tip These change the operations to be atomic. This means no operations can be performed on the value during the call.
Next This example familiarizes us with the syntax of Interlocked and Interlocked.Add.
Info There is no chance that thread 1 or thread 2 will read the value of the field before the other thread has written to it.
Warning Without Interlocked, both threads could read the value, then both change it afterwards. The result would be 1 not 2.
using System; using System.Threading; class Program { static int _value; static void Main() { Thread thread1 = new Thread(new ThreadStart(A)); Thread thread2 = new Thread(new ThreadStart(A)); thread1.Start(); thread2.Start(); thread1.Join(); thread2.Join(); Console.WriteLine(Program._value); } static void A() { // Add one. Interlocked.Add(ref Program._value, 1); } }
2
Increment, Decrement. Continuing on, the Interlocked type offers the Increment and Decrement methods as well. These methods are convenient, and they add or subtract 1.
Info These methods are much the same as Add, but they use the value of 1 as an implicit argument.
using System; using System.Threading; class Program { static int _value; static void Main() { Thread thread1 = new Thread(new ThreadStart(A)); Thread thread2 = new Thread(new ThreadStart(A)); thread1.Start(); thread2.Start(); thread1.Join(); thread2.Join(); Console.WriteLine(Program._value); } static void A() { // Add one then subtract two. Interlocked.Increment(ref Program._value); Interlocked.Decrement(ref Program._value); Interlocked.Decrement(ref Program._value); } }
-2
Exchange. Here we see how the Interlocked.Exchange method works. Exchange is essentially an assignment: the value we pass to it is changed to the argument.
Detail This is a conditional and an assignment. If the value equals the second argument, it is changed to the third argument.
using System; using System.Threading; class Program { static long _value1; static void Main() { Thread thread1 = new Thread(new ThreadStart(A)); thread1.Start(); thread1.Join(); // Written [2] Console.WriteLine(Interlocked.Read(ref Program._value1)); } static void A() { // Replace value with 10. Interlocked.Exchange(ref Program._value1, 10); // CompareExchange: if 10, change to 20. long result = Interlocked.CompareExchange(ref Program._value1, 20, 10); // Returns original value from CompareExchange [1] Console.WriteLine(result); } }
10 20
Performance. While calling Interlocked methods seems simpler in programs, does it actually perform faster than a lock? In this benchmark we time the 2 approaches in C#.
Version 1 We test a lock before an integer increment in the first loop. This code is longer, and does not use Interlocked.
Version 2 This is the second version of the code. We test a call to Interlocked.Increment in the second loop.
Here The result is correct because the value printed is equal to the total number of increment operations.
Result Interlocked.Increment was several times faster, requiring only 6 nanoseconds versus 40 nanoseconds for the lock construct.
using System; using System.Diagnostics; using System.Threading; class Program { static object _locker = new object(); static int _test; const int _max = 10000000; static void Main() { var s1 = Stopwatch.StartNew(); // Version 1: use lock. for (int i = 0; i < _max; i++) { lock (_locker) { _test++; } } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: use Interlocked. for (int i = 0; i < _max; i++) { Interlocked.Increment(ref _test); } s2.Stop(); Console.WriteLine(_test); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } }
20000000 40.02 ns 6.40 ns [Interlocked.Increment]
Summary. Once you get past the awkward syntax of the Interlocked methods, they can make writing multithreaded C# programs easier. You can avoid locking on certain values.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Aug 20, 2021 (simplify).
Home
Changes
© 2007-2024 Sam Allen.