Problem
What is the best approach to get a thread safe counter in C# that performs well?
This is as straightforward as it gets:
public static long GetNextValue()
{
long result;
lock (LOCK)
{
result = COUNTER++;
}
return result;
}
Is there, however, a faster way?
Asked by JohnDoDo
Solution #1
It’d be easier if:
return Interlocked.Increment(ref COUNTER);
MSDN Interlocked.Increment
Answered by Austin Salonen
Solution #2
Others have suggested that the Interlocked.Increment will perform better than lock (). Just look at the IL and Assembly to see how Increment becomes a “bus lock” statement and its variable is either directly incremented (x86) or “added” to (x64).
This “bus lock” statement locks the bus, preventing another CPU from accessing it while the caller CPU is performing its task. Take a peek at the IL for the C# lock() statement. Calls to Monitor to start or stop a section can be found here.
To put it another way, The statement net lock() accomplishes a lot more than the. Net Interlocked.Increment is a program that increases the value of a number by one.
If all you want to do is increase a variable, Interlock is the way to go. It will be easier to progress. Examine all of the Interlocked methods to discover what atomic operations are available and which ones are best for you. Use lock() to serialize access to resources that are more sophisticated than integers or to conduct more complex tasks like multiple inter-related increments/decrements.
Answered by Les
Solution #3
In the System, I recommend that you use.built-in NET’s interlock increment. a library for threading
The following code, which is thread safe, will increment a long variable by reference:
Interlocked.Increment(ref myNum);
Source: http://msdn.microsoft.com/en-us/library/dd78zt0c.aspx
Answered by Andrew White
Solution #4
Try with Interlocked.Increment
Answered by fsimonazzi
Solution #5
Use Interlocked as previously stated. Increment
A Microsoft code example:
To generate 1,000 random numbers with a midway value, the following example determines how many random numbers ranging from 0 to 1,000 are required. A variable, midpointCount, is assigned to 0 and incremented each time the random number generator delivers a midpoint value until it reaches 10,000 to keep track of the number of midpoint values. Because the random numbers are generated by three threads, the Increment(Int32) method is used to prevent several threads from updating midpointCount at the same time. It’s worth noting that a lock is also used to secure the random number generator, as well as a CountdownEvent object to ensure that the Main method doesn’t complete execution before the three threads.
using System;
using System.Threading;
public class Example
{
const int LOWERBOUND = 0;
const int UPPERBOUND = 1001;
static Object lockObj = new Object();
static Random rnd = new Random();
static CountdownEvent cte;
static int totalCount = 0;
static int totalMidpoint = 0;
static int midpointCount = 0;
public static void Main()
{
cte = new CountdownEvent(1);
// Start three threads.
for (int ctr = 0; ctr <= 2; ctr++) {
cte.AddCount();
Thread th = new Thread(GenerateNumbers);
th.Name = "Thread" + ctr.ToString();
th.Start();
}
cte.Signal();
cte.Wait();
Console.WriteLine();
Console.WriteLine("Total midpoint values: {0,10:N0} ({1:P3})",
totalMidpoint, totalMidpoint/((double)totalCount));
Console.WriteLine("Total number of values: {0,10:N0}",
totalCount);
}
private static void GenerateNumbers()
{
int midpoint = (UPPERBOUND - LOWERBOUND) / 2;
int value = 0;
int total = 0;
int midpt = 0;
do {
lock (lockObj) {
value = rnd.Next(LOWERBOUND, UPPERBOUND);
}
if (value == midpoint) {
Interlocked.Increment(ref midpointCount);
midpt++;
}
total++;
} while (midpointCount < 10000);
Interlocked.Add(ref totalCount, total);
Interlocked.Add(ref totalMidpoint, midpt);
string s = String.Format("Thread {0}:\n", Thread.CurrentThread.Name) +
String.Format(" Random Numbers: {0:N0}\n", total) +
String.Format(" Midpoint values: {0:N0} ({1:P3})", midpt,
((double) midpt)/total);
Console.WriteLine(s);
cte.Signal();
}
}
// The example displays output like the following:
// Thread Thread2:
// Random Numbers: 2,776,674
// Midpoint values: 2,773 (0.100 %)
// Thread Thread1:
// Random Numbers: 4,876,100
// Midpoint values: 4,873 (0.100 %)
// Thread Thread0:
// Random Numbers: 2,312,310
// Midpoint values: 2,354 (0.102 %)
//
// Total midpoint values: 10,000 (0.100 %)
// Total number of values: 9,965,084
The following example is identical to the last one, only it generates 50,000 random midway numbers using the Task class rather than a thread process. A lambda expression substitutes the GenerateNumbers thread method and the Task call in this example. The CountdownEvent object is no longer required because of the WaitAll method.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
const int LOWERBOUND = 0;
const int UPPERBOUND = 1001;
static Object lockObj = new Object();
static Random rnd = new Random();
static int totalCount = 0;
static int totalMidpoint = 0;
static int midpointCount = 0;
public static void Main()
{
List<Task> tasks = new List<Task>();
// Start three tasks.
for (int ctr = 0; ctr <= 2; ctr++)
tasks.Add(Task.Run( () => { int midpoint = (UPPERBOUND - LOWERBOUND) / 2;
int value = 0;
int total = 0;
int midpt = 0;
do {
lock (lockObj) {
value = rnd.Next(LOWERBOUND, UPPERBOUND);
}
if (value == midpoint) {
Interlocked.Increment(ref midpointCount);
midpt++;
}
total++;
} while (midpointCount < 50000);
Interlocked.Add(ref totalCount, total);
Interlocked.Add(ref totalMidpoint, midpt);
string s = String.Format("Task {0}:\n", Task.CurrentId) +
String.Format(" Random Numbers: {0:N0}\n", total) +
String.Format(" Midpoint values: {0:N0} ({1:P3})", midpt,
((double) midpt)/total);
Console.WriteLine(s); } ));
Task.WaitAll(tasks.ToArray());
Console.WriteLine();
Console.WriteLine("Total midpoint values: {0,10:N0} ({1:P3})",
totalMidpoint, totalMidpoint/((double)totalCount));
Console.WriteLine("Total number of values: {0,10:N0}",
totalCount);
}
}
// The example displays output like the following:
// Task 3:
// Random Numbers: 10,855,250
// Midpoint values: 10,823 (0.100 %)
// Task 1:
// Random Numbers: 15,243,703
// Midpoint values: 15,110 (0.099 %)
// Task 2:
// Random Numbers: 24,107,425
// Midpoint values: 24,067 (0.100 %)
//
// Total midpoint values: 50,000 (0.100 %)
// Total number of values: 50,206,378
Answered by Ogglas
Post is based on https://stackoverflow.com/questions/13181740/c-sharp-thread-safe-fastest-counter