web-dev-qa-db-fra.com

Pourquoi Thread.Sleep est-il si dangereux?

Je vois souvent qu'il est mentionné que Thread.Sleep(); ne devrait pas être utilisé, mais je ne comprends pas pourquoi. Si Thread.Sleep(); peut causer des problèmes, existe-t-il des solutions de rechange avec le même résultat qui seraient sans danger?

par exemple.

while(true)
{
    doSomework();
    i++;
    Thread.Sleep(5000);
}

un autre est:

while (true)
{
    string[] images = Directory.GetFiles(@"C:\Dir", "*.png");

    foreach (string image in images)
    {
        this.Invoke(() => this.Enabled = true);
        pictureBox1.Image = new Bitmap(image);
        Thread.Sleep(1000);
    }
}
112
Burimi

Les problèmes liés à l'appel de Thread.Sleep sont expliqués ici de manière succincte :

Thread.Sleep a son utilité: simuler de longues opérations lors du test/débogage sur un thread MTA. En .NET, il n'y a aucune autre raison de l'utiliser.

Thread.Sleep(n) signifie bloquer le thread en cours pendant au moins le nombre de tranches de temps (ou quantum de thread) pouvant se produire dans n millisecondes. La longueur d'une tranche de temps est différente selon les versions/types de Windows et les processeurs et varie généralement entre 15 et 30 millisecondes. Cela signifie que le thread est presque toujours bloqué pendant plus de n millisecondes. La probabilité que votre thread se réveille exactement après n millisecondes est à peu près aussi impossible qu'impossible. Donc, Thread.Sleep est inutile pour le chronométrage .

Les threads sont une ressource limitée. Ils nécessitent environ 200 000 cycles de création et environ 100 000 cycles de destruction. Par défaut, ils réservent 1 Mo de mémoire virtuelle pour sa pile et utilisent 2 000 à 8 000 cycles pour chaque commutateur de contexte. Ceci fait de tout thread en attente un énorme gaspillage.

La solution préférée: WaitHandles

L’erreur la plus commise est d’utiliser Thread.Sleep avec une construction while ( demo and answer , belle entrée de blog )

EDIT:
Je voudrais améliorer ma réponse:

Nous avons 2 cas d'utilisation différents:

  1. Nous attendons parce que nous connaissons un laps de temps spécifique pendant lequel nous devrions continuer (utilisez Thread.Sleep, System.Threading.Timer ou de la même manière)

  2. Nous attendons car certaines conditions changent quelque temps ... le ou les mots-clés sont/sont quelque temps ! si la vérification de condition est dans notre code-domaine, nous devrions utiliser WaitHandles - sinon le composant externe devrait fournir une sorte de hook ... sinon, sa conception est mauvaise!

Ma réponse couvre principalement le cas d'utilisation 2

149
Andreas Niedermair

SCÉNARIO 1 - attendre la fin de la tâche asynchrone: je conviens que WaitHandle/Auto | ManualResetEvent doit être utilisé dans le cas où un thread attend la fin de la tâche sur un autre thread.

SCÉNARIO 2 - minutage en boucle: Cependant, comme mécanisme brut (while + Thread.Sleep) convient parfaitement pour 99% des applications qui ne nécessitent PAS de savoir exactement quand le thread bloqué doit "réveiller *. L'argument selon lequel il faut 200 k cycles pour créer le fil est également invalide - le fil de la boucle de synchronisation doit être créé de toute façon et 200 k cycles est juste un autre grand nombre (dites-moi combien de cycles pour ouvrir un fichier/socket/db appelle?).

Donc, si pendant que + Thread.Sleep fonctionne, pourquoi compliquer les choses? Seuls les juristes de la syntaxe pourraient être pratique!

32
Swab.Jat

J'aimerais répondre à cette question du point de vue de la politique de codage, ce qui peut être utile ou non. Mais surtout lorsque vous utilisez des outils destinés aux programmeurs d'entreprise 9 à 5 ans, les rédacteurs de documentation ont tendance à utiliser des mots tels que "ne devrait pas" et "jamais" pour "ne le faites pas si vous ne savez pas vraiment ce que vous faites." fais et pourquoi ".

Quelques-uns de mes autres favoris dans le monde C # sont qu'ils vous disent de "ne jamais appeler lock (this)" ou de "jamais appeler GC.Collect ()". Ces deux éléments sont déclarés avec force dans de nombreux blogs et documents officiels, et l’OMI est une désinformation complète. À un certain niveau, cette information erronée a son utilité, car elle empêche les débutants de faire des choses qu'ils ne comprennent pas avant de rechercher les solutions de rechange, mais il est en même temps difficile de trouver de VRAIE information via des moteurs de recherche. semblent indiquer que des articles vous disent de ne pas faire quelque chose sans offrir de réponse à la question "pourquoi pas?"

Politiquement, cela revient à ce que les gens considèrent comme un "bon design" ou un "mauvais design". La documentation officielle ne devrait pas dicter la conception de mon application. S'il existe vraiment une raison technique pour ne pas appeler sleep (), la documentation d'OMI doit indiquer qu'il est tout à fait possible de l'appeler dans des scénarios spécifiques, mais peut-être proposer des solutions alternatives indépendantes de chaque scénario ou plus appropriées pour l'autre. scénarios.

Il est clairement utile d’appeler "sleep ()" dans de nombreuses situations lorsque les délais sont clairement définis, mais il existe des systèmes plus sophistiqués d’attente et de signalisation qui doivent être pris en compte et compris avant de commencer à vous endormir ( ) dans votre code, et jeter des instructions sleep () inutiles dans votre code est généralement considéré comme une tactique pour les débutants.

8
Jason Nelson

La veille est utilisée dans les cas où un programme indépendant sur lequel vous n'avez aucun contrôle peut parfois utiliser une ressource couramment utilisée (un fichier, par exemple), auquel votre programme doit accéder lorsqu'il s'exécute et lorsque la ressource est utilisée par ceux-ci. d’autres programmes, votre programme n’est pas autorisé à l’utiliser. Dans ce cas, lorsque vous accédez à la ressource dans votre code, vous placez votre accès de la ressource dans un try-catch (pour intercepter l'exception lorsque vous ne pouvez pas accéder à la ressource) et vous le mettez dans une boucle while. Si la ressource est libre, le sommeil n'est jamais appelé. Mais si la ressource est bloquée, vous dormez pendant un laps de temps approprié et tentez à nouveau d'accéder à la ressource (c'est pourquoi vous bouclez). Cependant, gardez à l'esprit que vous devez mettre une sorte de limiteur sur la boucle, de sorte que ce ne soit pas une boucle potentiellement infinie. Vous pouvez définir votre condition limite sur N nombre de tentatives (c'est ce que j'utilise habituellement) ou vérifier l'horloge système, ajouter une durée fixe pour obtenir une limite de temps et cesser de tenter l'accès si vous respectez cette limite.

3
Steve Greene

Il s'agit de la boucle 1) .spinning et 2) .polling de vos exemples contre laquelle les gens mettent en garde contre , et non la partie Thread.Sleep (). Je pense que Thread.Sleep () est généralement ajouté pour améliorer facilement le code en train de tourner ou dans une boucle de scrutation, il est donc simplement associé au code "incorrect".

De plus, les gens font des choses comme:

while(inWait)Thread.Sleep(5000); 

où la variable inWait n’est pas accédée de manière thread-safe, ce qui pose également des problèmes.

Ce que les programmeurs veulent voir, ce sont les threads contrôlés par les constructions Events et Signaling and Locking. Vous n'aurez alors plus besoin de Thread.Sleep () et les préoccupations relatives à l'accès aux variables thread-safe seront également éliminées. Par exemple, pourriez-vous créer un gestionnaire d'événements associé à la classe FileSystemWatcher et utiliser un événement pour déclencher votre deuxième exemple au lieu de boucler?

Comme Andreas N. l’a mentionné, lisez Threading in C #, de Joe Albahari , c’est vraiment très bon.

3
mike