Al solicitar Queue.Synchonized
se obtiene una SynchronizedQueue
a cambio que utiliza un lock
mínimamente alrededor llama a Enqueue
y Dequeue
en una cola interna. Por lo tanto, el rendimiento debe ser el mismo que usar un Queue
y administrar el bloqueo usted mismo para Enqueue
y Dequeue
con su propio lock
.
De hecho, estás imaginando cosas, deberían ser lo mismo.
actualización
No es en realidad el hecho de que cuando se utiliza un SynchronizedQueue
va a añadir una capa de direccionamiento indirecto ya que tienes que ir a través de los métodos de contenedor para llegar a la cola interna que esté administrando. En todo caso, esto debería ralentizar las cosas muy poco, ya que tienes un marco extra en la pila que necesita ser administrado para cada llamada. Sin embargo, Dios sabe si el forro interior cancela esto. Lo que sea - es mínimo.
Actualización 2
ahora han referenciado esto, y como se predijo en mi anterior actualización
"Queue.Synchronized" es más lenta que la "cola + lock"
He llevado a cabo una prueba de un solo hilo ya que ambos usan la misma técnica de bloqueo (es decir, lock
) por lo que probar la sobrecarga pura en una "línea recta" parece razonable.
Mi referencia produjo los siguientes resultados para un lanzamiento construcción:
Iterations :10,000,000
Queue+Lock :539.14ms
Queue+Lock :540.55ms
Queue+Lock :539.46ms
Queue+Lock :540.46ms
Queue+Lock :539.75ms
SynchonizedQueue:578.67ms
SynchonizedQueue:585.04ms
SynchonizedQueue:580.22ms
SynchonizedQueue:578.35ms
SynchonizedQueue:578.57ms
usando el siguiente código:
private readonly object _syncObj = new object();
[Test]
public object measure_queue_locking_performance()
{
const int TestIterations = 5;
const int Iterations = (10 * 1000 * 1000);
Action<string, Action> time = (name, test) =>
{
for (int i = 0; i < TestIterations; i++)
{
TimeSpan elapsed = TimeTest(test, Iterations);
Console.WriteLine("{0}:{1:F2}ms", name, elapsed.TotalMilliseconds);
}
};
object itemOut, itemIn = new object();
Queue queue = new Queue();
Queue syncQueue = Queue.Synchronized(queue);
Action test1 =() =>
{
lock (_syncObj) queue.Enqueue(itemIn);
lock (_syncObj) itemOut = queue.Dequeue();
};
Action test2 =() =>
{
syncQueue.Enqueue(itemIn);
itemOut = syncQueue.Dequeue();
};
Console.WriteLine("Iterations:{0:0,0}\r\n", Iterations);
time("Queue+Lock", test1);
time("SynchonizedQueue", test2);
return itemOut;
}
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.GC.Collect")]
private static TimeSpan TimeTest(Action action, int iterations)
{
Action gc =() =>
{
GC.Collect();
GC.WaitForFullGCComplete();
};
Action empty =() => { };
Stopwatch stopwatch1 = Stopwatch.StartNew();
for (int j = 0; j < iterations; j++)
{
empty();
}
TimeSpan loopElapsed = stopwatch1.Elapsed;
gc();
action(); //JIT
action(); //Optimize
Stopwatch stopwatch2 = Stopwatch.StartNew();
for (int j = 0; j < iterations; j++) action();
gc();
TimeSpan testElapsed = stopwatch2.Elapsed;
return (testElapsed - loopElapsed);
}
Simplemente sacando una cerradura con cualquiera de los métodos es increíblemente rápido. El golpe de rendimiento proviene de cuánta contención hay para los bloqueos. – serg10