que implementa el siguiente hilo de procesamiento en segundo plano, donde Jobs
es una Queue<T>
:ManualResetEvent vs Thread.Sleep
static void WorkThread()
{
while (working)
{
var job;
lock (Jobs)
{
if (Jobs.Count > 0)
job = Jobs.Dequeue();
}
if (job == null)
{
Thread.Sleep(1);
}
else
{
// [snip]: Process job.
}
}
}
Esto produjo un retraso notable entre el momento estaban siendo introducidos los puestos de trabajo y, cuando en realidad estaban empezando a ser ejecutar (los lotes de trabajos se ingresan a la vez, y cada trabajo es solo [relativamente] pequeño). La demora no fue un gran problema, pero llegué a pensar en el problema e hice el siguiente cambio:
static ManualResetEvent _workerWait = new ManualResetEvent(false);
// ...
if (job == null)
{
lock (_workerWait)
{
_workerWait.Reset();
}
_workerWait.WaitOne();
}
Donde el hilo la adición de trabajos ahora bloquea _workerWait
y llama al _workerWait.Set()
cuando termina de agregar trabajos. Esta solución (aparentemente) instantáneamente comienza a procesar trabajos, y la demora se ha ido por completo.
Mi pregunta es en parte "¿Por qué sucede esto?", Dado que Thread.Sleep(int)
puede dormir mucho más de lo que especifique, y en parte "¿Cómo logra el ManualResetEvent
este nivel de rendimiento?".
EDIT: Dado que alguien le preguntó acerca de la función que está haciendo cola de elementos, aquí está, junto con el sistema completo tal como está en este momento.
public void RunTriggers(string data)
{
lock (this.SyncRoot)
{
this.Triggers.Sort((a, b) => { return a.Priority - b.Priority; });
foreach (Trigger trigger in this.Triggers)
{
lock (Jobs)
{
Jobs.Enqueue(new TriggerData(this, trigger, data));
_workerWait.Set();
}
}
}
}
static private ManualResetEvent _workerWait = new ManualResetEvent(false);
static void WorkThread()
{
while (working)
{
TriggerData job = null;
lock (Jobs)
{
if (Jobs.Count > 0)
job = Jobs.Dequeue();
if (job == null)
{
_workerWait.Reset();
}
}
if (job == null)
_workerWait.WaitOne();
else
{
try
{
foreach (Match m in job.Trigger.Regex.Matches(job.Data))
job.Trigger.Value.Action(job.World, m);
}
catch (Exception ex)
{
job.World.SendLineToClient("\r\n\x1B[32m -- {0} in trigger ({1}): {2}\x1B[m",
ex.GetType().ToString(), job.Trigger.Name, ex.Message);
}
}
}
}
Información más reciente: la resolución mínima de 10 ms es una XP y algo más temprano ya que el sistema operativo usa incrementos estáticos de 10 ms para la programación. Creo que Vista, y sé que Win7 sí, usa un segmento de tiempo dinámico "sin tictac". Con Win7, puedo iniciar un temporizador de alta resolución, emite un sueño (1), y el tiempo es extremadamente cercano a 1 ms, a veces menor que. – Bengie