Este código lo vi por casualidad. El código es más o menos como sigue.
public class Question2 { public void Run() { List<Task> tasks = new List<Task>(); for (int i = 0; i < 10000; i++) { tasks.Add(Task.Factory.StartNew(() => { GetNextId(); })); } Task.WaitAll(tasks.ToArray()); Console.WriteLine("currentId=" + GetCurrentId()); Console.ReadKey(); } private int currentId; public int GetNextId() { currentId++; return currentId; } public int GetCurrentId() { return currentId; } }No sé por qué el resultado no es 10000. Alguien me puede decir porque y como corregirlo.
¡Acabas de descubrir el tema de la seguridad de subprocesos! currentId++ traduce como
Ahora imagine que su ciclo va solo hasta i < 3. El primer hilo está en el paso 1 y lee el valor 0. Antes de pasar al paso 2, el segundo hilo está en el paso 1 y también lee 0. Ahora ambos hilos van al paso 3, escribirá ambos 1 en la memoria. Ahora comienza el tercer hilo y leerá 1 de la memoria, lo incrementará a 2 y lo escribirá en la memoria. Ahora tiene 2 en lugar del valor esperado 3.
Para solucionar esto, puede usar Interlocked.Increment :
private int currentId; public int GetNextId() { return Interlocked.Increment(ref currentId); } Pero tenga en cuenta que, dependiendo de su caso de uso, es posible que aún tenga problemas similares cuando use el valor de retorno de GetNextId() .
currentId++;En un programa de un solo subproceso, la secuencia de eventos se ve así.
En su programa de subprocesos múltiples, la secuencia de eventos puede parecerse más a esto.
Entonces, el valor de currentId se incrementó tres veces, pero el valor es 1000.
Una solución a este problema es usar la palabra clave lock. El uso de la palabra clave lock garantiza que solo un subproceso tenga acceso a un fragmento de código a la vez.
private int currentId; private object _object = new object(); public int GetNextId() { lock (_object) { currentId++; return currentId; } }La secuencia de eventos ahora se ve así.
Tenga en cuenta que el resultado seguirá siendo 9999 si el valor inicial es cero y lo incrementa 9999 veces.