70 votes

SpinWait vs sommeil en attente. Lequel utiliser?

Est-il efficace pour

SpinWait.SpinUntil(() => myPredicate(), 10000)

pour un délai d'attente de 10000ms

ou

Est-il plus efficace d'utiliser Thread.Sleep de vote pour le même état Par exemple quelque chose le long des lignes suivantes SleepWait fonction de:

public bool SleepWait(int timeOut)
{
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start();
    while (!myPredicate() && stopwatch.ElapsedMilliseconds < timeOut)
    {
       Thread.Sleep(50)
    }  
    return myPredicate()
}

Je suis préoccupé de ce que tous le rendement de l'SpinWait ne peut pas être un bon modèle d'utilisation, si nous parlons de délais d'attente de plus de 1sec? Est-ce une hypothèse valide?

L'approche qui préférez-vous et pourquoi? Est-il une autre encore meilleure approche?


Mise à jour - Deviennent de plus en plus spécifiques:

Est-il un moyen de Faire BlockingCollection Impulsion d'un thread en sommeil lorsqu'il atteint délimitée capacité? Je suis plutôt d'éviter une longue attend en tout comme Marc Gravier suggère.

124voto

Killercam Points 9388

Dans .NET 4 SpinWait effectue gourmandes en filature pendant 10 itérations avant de céder. Mais il ne veut pas retourner à l'appelant immédiatement après chacune de ces cycles; au lieu de cela, il appelle Thread.SpinWait de spin par le CLR (essentiellement les OS) pour une période de temps de jeu. Cette période de temps est d'abord quelques dizaines de nano-secondes, mais double à chaque itération jusqu'à ce que les 10 itérations sont complètes. Cette mesure de la clarté et la prévisibilité dans le temps total passé à la filature (CPU-intensive) de phase, le système peut régler selon les conditions (nombre de cœurs etc.). Si SpinWait reste dans le spin-rendement phase pendant trop longtemps, il va régulièrement de sommeil pour permettre à d'autres threads de procéder (voir J. Albahari du blog pour plus d'informations). Ce processus est garanti pour garder un cœur occupé...

Donc, SpinWait limite le PROCESSEUR tourne à un certain nombre d'itérations, après quoi il donne de sa tranche de temps sur chaque tour (en fait, l'appelant Thread.Yield et Thread.Sleep), en diminuant sa consommation de ressources. Il permettra également de détecter si l'utilisateur est en cours d'exécution d'un seul noyau de la machine et le rendement sur chaque cycle si c'est le cas.

Avec Thread.Sleep le thread est bloqué. Mais ce processus ne sera pas aussi cher que le ci-dessus en termes de CPU.

45voto

Marc Gravell Points 482669

La meilleure approche est d'avoir un mécanisme pour détecter activement la chose devient vrai (plutôt que passivement bureaux de pour avoir devenir true); ce pourrait être n'importe quel type d'attendre poignée, ou peut-être un Task avec Wait, ou peut-être un event que vous pouvez vous abonner à décoller vous-même. Bien sûr, si vous faites ce genre de "attendre jusqu'à ce qu'il se passe quelque chose", c'est toujours pas aussi efficace que d'avoir simplement le prochain peu de travail à faire , comme un rappel, sens: vous n'avez pas besoin d'utiliser un thread pour attendre. Task a ContinueWith pour cela, ou vous pouvez simplement faire le travail dans un event lorsqu'il est exclu. L' event est probablement l'approche la plus simple, selon le contexte. Task, cependant, fournit déjà plus tout ce que vous êtes en train de parler, y compris à la fois "attendre avec timeout" et "rappel" des mécanismes.

Et oui, filature pendant 10 secondes n'est pas grande. Si vous voulez utiliser quelque chose comme votre code actuel, et si vous avez des raisons de s'attendre à un délai court, mais qui nécessite plus d'un peut-être SpinWait pour (dire) 20ms, et utiliser Sleep pour le reste?


Re le commentaire; voici comment je ferais un crochet "c'est complet" mécanisme:

private readonly object syncLock = new object();
public bool WaitUntilFull(int timeout) {
    if(CollectionIsFull) return true; // I'm assuming we can call this safely
    lock(syncLock) {
        if(CollectionIsFull) return true;
        return Monitor.Wait(syncLock, timeout);
    }
}

avec, dans le "remettre dans la collection" code:

if(CollectionIsFull) {
    lock(syncLock) {
        if(CollectionIsFull) { // double-check with the lock
            Monitor.PulseAll(syncLock);
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X