Risposte:
Come raccomandato da altri, Interlocked.Increment
avrà prestazioni migliori rispetto a lock()
. Dai un'occhiata a IL e Assembly in cui vedrai che Increment
si trasforma in un'istruzione "blocco bus" e la sua variabile viene incrementata direttamente (x86) o "aggiunta" a (x64).
Questa istruzione "blocco bus" blocca il bus per impedire ad un'altra CPU di accedere al bus mentre la CPU chiamante fa il suo funzionamento. Ora dai un'occhiata lock()
all'IL dell'istruzione C # . Qui vedrai le chiamate Monitor
per iniziare o terminare una sezione.
In altre parole, l' lock()
istruzione .Net sta facendo molto di più di .Net Interlocked.Increment
.
Quindi, se tutto ciò che vuoi fare è incrementare una variabile, Interlock.Increment
sarà più veloce. Esamina tutti i metodi interbloccati per vedere le varie operazioni atomiche disponibili e per trovare quelle che soddisfano le tue esigenze. Utilizzare lock()
quando si desidera eseguire operazioni più complesse come multipli incrementi / decrementi correlati o per serializzare l'accesso a risorse più complesse degli interi.
Ti suggerisco di utilizzare l'incremento di interblocco incorporato di .NET nella libreria System.Threading.
Il codice seguente incrementerà una variabile lunga per riferimento ed è completamente sicuro per i thread:
Interlocked.Increment(ref myNum);
Fonte: http://msdn.microsoft.com/en-us/library/dd78zt0c.aspx
Prova con Interlocked.Increment
Come già accennato usare Interlocked.Increment
Esempio di codice da MS:
L'esempio seguente determina quanti numeri casuali che vanno da 0 a 1.000 sono necessari per generare 1.000 numeri casuali con un valore di punto medio. Per tenere traccia del numero di valori del punto medio, una variabile, punto intermedio, viene impostata uguale a 0 e incrementata ogni volta che il generatore di numeri casuali restituisce un valore del punto medio fino a raggiungere 10.000. Poiché tre thread generano i numeri casuali, viene chiamato il metodo Increment (Int32) per garantire che più thread non aggiornino midpointCount contemporaneamente. Si noti che un blocco viene utilizzato anche per proteggere il generatore di numeri casuali e che un oggetto CountdownEvent viene utilizzato per garantire che il metodo Main non completi l'esecuzione prima dei tre thread.
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
L'esempio seguente è simile al precedente, tranne per il fatto che utilizza la classe Task anziché una procedura di thread per generare 50.000 numeri interi casuali del punto medio. In questo esempio, un'espressione lambda sostituisce la procedura del thread GenerateNumbers e la chiamata al metodo Task.WaitAll elimina la necessità dell'oggetto CountdownEvent.
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
https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.increment?view=netcore-3.0