Respuestas:
Según lo recomendado por otros, Interlocked.Increment
tendrá un mejor rendimiento que lock()
. Solo eche un vistazo a IL y Assembly, donde verá que se Increment
convierte en una declaración de "bloqueo de bus" y su variable se incrementa directamente (x86) o "se agrega" a (x64).
Esta declaración de "bloqueo de bus" bloquea el bus para evitar que otra CPU acceda al bus mientras la CPU que llama realiza su operación. Ahora, eche un vistazo a la lock()
IL de la declaración de C # . Aquí verá llamadas Monitor
para comenzar o finalizar una sección.
En otras palabras, la lock()
declaración .Net está haciendo mucho más que la .Net Interlocked.Increment
.
Entonces, si todo lo que quieres hacer es incrementar una variable, Interlock.Increment
será más rápido. Revise todos los métodos enclavados para ver las diversas operaciones atómicas disponibles y para encontrar las que se adapten a sus necesidades. Úselo lock()
cuando desee hacer cosas más complejas, como múltiples incrementos / decrementos interrelacionados, o para serializar el acceso a recursos que son más complejos que los enteros.
Le sugiero que use el incremento de enclavamiento integrado de .NET en la biblioteca System.Threading.
El siguiente código incrementará una variable larga por referencia y es completamente seguro para subprocesos:
Interlocked.Increment(ref myNum);
Fuente: http://msdn.microsoft.com/en-us/library/dd78zt0c.aspx
Prueba con Interlocked.
Como ya se mencionó uso Interlocked.Increment
Ejemplo de código de MS:
El siguiente ejemplo determina cuántos números aleatorios que van de 0 a 1,000 son necesarios para generar 1,000 números aleatorios con un valor de punto medio. Para realizar un seguimiento del número de valores de punto medio, una variable, midpointCount, se establece igual a 0 y se incrementa cada vez que el generador de números aleatorios devuelve un valor de punto medio hasta llegar a 10,000. Debido a que tres subprocesos generan los números aleatorios, se llama al método Incremento (Int32) para garantizar que varios subprocesos no actualicen midpointCount simultáneamente. Tenga en cuenta que también se usa un bloqueo para proteger el generador de números aleatorios, y que se usa un objeto CountdownEvent para garantizar que el método Main no termine la ejecución antes de los tres subprocesos.
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
El siguiente ejemplo es similar al anterior, excepto que usa la clase Task en lugar de un procedimiento de subproceso para generar 50,000 enteros de punto medio aleatorio. En este ejemplo, una expresión lambda reemplaza el procedimiento de hilo GenerateNumbers y la llamada al método Task.WaitAll elimina la necesidad del objeto 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