I mis en œuvre le fil de traitement de fond suivant, où Jobs
est un Queue<T>
:ManualResetEvent contre 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.
}
}
}
Ce produit un retard notable entre le moment où les emplois étaient entrés et quand ils ont été effectivement commencé à être exécuter (des lots d'emplois sont entrés en même temps, et chaque travail est seulement [relativement] petit.) Le retard n'était pas énorme, mais j'ai pensé au problème et j'ai apporté le changement suivant:
static ManualResetEvent _workerWait = new ManualResetEvent(false);
// ...
if (job == null)
{
lock (_workerWait)
{
_workerWait.Reset();
}
_workerWait.WaitOne();
}
Où le fil l'ajout de travaux verrouille désormais _workerWait
et appelle _workerWait.Set()
lorsqu'il a terminé d'ajouter des tâches. Cette solution (apparemment) démarre instantanément les tâches de traitement, et le retard est complètement disparu.
Ma question est en partie "Pourquoi cela arrive-t-il?", Sachant que Thread.Sleep(int)
peut très bien dormir plus longtemps que vous ne le spécifiez, et en partie "Comment le ManualResetEvent
atteint-il ce niveau de performance?".
EDIT: Depuis que quelqu'un a demandé à propos de la fonction qui met en file d'attente les éléments, le voici, avec le système complet tel qu'il se présente pour l'instant.
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);
}
}
}
}
Information la plus récente: la résolution minimale de 10ms est une XP et une chose antérieure car le système d'exploitation utilisait des incréments statiques de 10ms pour la programmation. Je pense que Vista, et je sais que Win7 le fait, utilise une tranche de temps dynamique "tickless". Avec Win7, je peux démarrer une minuterie haute résolution, émettre un sleep (1), et le timing est extrêmement proche de 1ms, parfois inférieur à. – Bengie