Home
Search
C# Interlocked ExamplesUse the Interlocked type to change fields in a thread-safe way.
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.
C# program that uses Interlocked.Add
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.
C# program that uses Interlocked.Increment and Decrement
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.
CompareExchange This is a conditional and an assignment. If the value equals the second argument, it is changed to the third argument.
C# program that uses Interlocked
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.
C# program that tests Interlocked performance
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.
Home
© 2007-2021 sam allen. see site info on the changelog