C#
Summary - Thread and ThreadPool
Combine Thread, lock and Interlocked to write a counter.
Summary - Thread and ThreadPool
In this group you learned the Thread class, Start/Join synchronization, ThreadPool, the lock statement and Interlocked.
These are the foundations of low-level parallel programming. Modern C# prefers Task/async in most cases, but Thread, lock and Interlocked are still important tools. Let's now write two counter examples.
Exercise Increment 5 times each with lock
Declare private static readonly object _sync = new object(); and private static int _counter = 0;. In Main create 2 threads, each calling for (int i = 0; i < 5; i++) { lock (_sync) { _counter++; } }. Wait with Join. Print Console.WriteLine($"Counter: {_counter}");. (2 threads * 5 increments = 10 expected.)
Show example solution
Thread t1 = new Thread(() => { for (int i = 0; i < 5; i++) { lock (_sync) { _counter++; } } }); Thread t2 = new Thread(() => { for (int i = 0; i < 5; i++) { lock (_sync) { _counter++; } } }); t1.Start(); t2.Start(); t1.Join(); t2.Join(); Console.WriteLine($"Counter: {_counter}");
Example solution:
using System;
using System.Threading;
class Program
{
private static readonly object _sync = new object();
private static int _counter = 0;
static void Main()
{
Thread t1 = new Thread(() => {
for (int i = 0; i < 5; i++) { lock (_sync) { _counter++; } }
});
Thread t2 = new Thread(() => {
for (int i = 0; i < 5; i++) { lock (_sync) { _counter++; } }
});
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine($"Counter: {_counter}");
}
}
ThreadPool and Interlocked together
When you keep a shared counter across work items on the ThreadPool, Interlocked is the cleanest solution. It is much faster than a lock; each item runs on its own thread but Interlocked guarantees atomicity.
This pattern fits, for example, keeping a request counter in a web server. The code is both safe and high-performance.
Exercise Increment 3 times each with Interlocked
Declare private static int _counter = 0;. In Main create 2 threads, each calling for (int i = 0; i < 3; i++) { Interlocked.Increment(ref _counter); }. Wait with Join. Print Console.WriteLine($"Counter: {_counter}");. (2 threads * 3 increments = 6 expected.)
Show example solution
Thread t1 = new Thread(() => { for (int i = 0; i < 3; i++) { Interlocked.Increment(ref _counter); } }); Thread t2 = new Thread(() => { for (int i = 0; i < 3; i++) { Interlocked.Increment(ref _counter); } }); t1.Start(); t2.Start(); t1.Join(); t2.Join(); Console.WriteLine($"Counter: {_counter}");
Example solution:
using System;
using System.Threading;
class Program
{
private static int _counter = 0;
static void Main()
{
Thread t1 = new Thread(() => {
for (int i = 0; i < 3; i++) { Interlocked.Increment(ref _counter); }
});
Thread t2 = new Thread(() => {
for (int i = 0; i < 3; i++) { Interlocked.Increment(ref _counter); }
});
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine($"Counter: {_counter}");
}
}
Test yourself
-
Which provides atomic increment without a lock?
- counter++
- Interlocked.Increment
- lock (counter)
- Thread.Sleep
Show answer
Interlocked.Increment — Correct. Interlocked.Increment atomically increments without a lock.
-
Match the concepts
- Thread
- Separate thread
- ThreadPool
- Ready pool
- lock
- Critical section
- Interlocked
- Atomic operation
Next lesson: ConcurrentDictionary