Malheureusement, il n'y a peut-être pas de meilleure option. Cela dépend vraiment de votre scénario spécifique. L'idée est d'arrêter le fil de discussion de manière élégante à des points sûrs. C'est la raison principale pour laquelle Thread.Abort
n'est pas bon, car il n'est pas garanti qu'il se produise à des moments sûrs. En saupoudrant le code d'un mécanisme d'arrêt, vous définissez effectivement manuellement les points sûrs. C'est ce qu'on appelle l'annulation coopérative. Il existe quatre grands mécanismes pour ce faire. Vous pouvez choisir celui qui convient le mieux à votre situation.
Interroger un drapeau d'arrêt
Vous avez déjà mentionné cette méthode. C'est une méthode assez courante. Effectuez des vérifications périodiques du drapeau à des points sûrs de votre algorithme et renoncez à l'utiliser lorsqu'il est signalé. L'approche standard consiste à marquer la variable volatile
. Si cela n'est pas possible ou peu commode, vous pouvez utiliser une lock
. Rappelez-vous que vous ne pouvez pas marquer une variable locale comme volatile
Ainsi, si une expression lambda la capture par le biais d'une fermeture, par exemple, vous devrez recourir à une méthode différente pour créer la barrière mémoire requise. Il n'y a pas grand-chose d'autre à dire sur cette méthode.
Utiliser les nouveaux mécanismes d'annulation dans le TPL
C'est similaire à l'interrogation d'un drapeau d'arrêt, sauf qu'il utilise les nouvelles structures de données d'annulation dans la TPL. Elle est toujours basée sur des modèles d'annulation coopératifs. Vous devez obtenir un CancellationToken
et le contrôle périodique IsCancellationRequested
. Pour demander l'annulation, vous devez appeler Cancel
sur le CancellationTokenSource
qui a fourni le jeton à l'origine. Il y a beaucoup de choses que vous pouvez faire avec les nouveaux mécanismes d'annulation. Vous pouvez en savoir plus sur ici .
Utiliser les poignées d'attente
Cette méthode peut être utile si votre fil de travail doit attendre un intervalle spécifique ou un signal pendant son fonctionnement normal. Vous pouvez Set
a ManualResetEvent
par exemple, pour faire savoir au fil qu'il est temps de s'arrêter. Vous pouvez tester l'événement en utilisant la fonction WaitOne
qui renvoie un bool
indiquant si l'événement a été signalé. Le site WaitOne
prend un paramètre qui spécifie le temps d'attente pour le retour de l'appel si l'événement n'a pas été signalé dans ce laps de temps. Vous pouvez utiliser cette technique à la place de Thread.Sleep
et obtenir l'indication d'arrêt en même temps. Il est également utile s'il y a d'autres WaitHandle
que le thread peut avoir à attendre. Vous pouvez appeler WaitHandle.WaitAny
pour attendre n'importe quel événement (y compris l'événement d'arrêt) en un seul appel. Utiliser un événement peut être plus efficace que d'appeler Thread.Interrupt
puisque vous avez plus de contrôle sur le déroulement du programme ( Thread.Interrupt
lève une exception, il faut donc placer stratégiquement l'option try-catch
pour effectuer tout nettoyage nécessaire).
Scénarios spécialisés
Il y a plusieurs scénarios ponctuels qui ont des mécanismes d'arrêt très spécialisés. Il n'entre pas dans le cadre de cette réponse de les énumérer tous (sans compter que ce serait presque impossible). Un bon exemple de ce que je veux dire ici est le scénario Socket
la classe. Si le thread est bloqué lors d'un appel à Send
o Receive
puis en appelant Close
interrompra la socket sur n'importe quel appel bloquant dans lequel elle se trouvait, la débloquant effectivement. Je suis sûr qu'il y a plusieurs autres endroits dans la BCL où des techniques similaires peuvent être utilisées pour débloquer un thread.
Interrompre le fil de discussion via Thread.Interrupt
L'avantage ici est qu'il est simple et que vous ne devez pas vous attacher à saupoudrer votre code de quoi que ce soit. L'inconvénient est que vous avez peu de contrôle sur l'emplacement des points sûrs dans votre algorithme. La raison en est que Thread.Interrupt
fonctionne en injectant une exception dans l'un des appels bloquants de la BCL. Ces appels comprennent Thread.Sleep
, WaitHandle.WaitOne
, Thread.Join
etc. Vous devez donc faire attention à l'endroit où vous les placez. Cependant, la plupart du temps, c'est l'algorithme qui dicte leur emplacement et c'est généralement bien, surtout si votre algorithme passe la plupart de son temps dans l'un de ces appels bloquants. Si votre algorithme n'utilise pas l'un des appels bloquants de la BCL, cette méthode ne vous conviendra pas. La théorie ici est que le ThreadInterruptException
est seulement généré à partir de l'appel d'attente .NET, il est donc probablement à un point sûr. Vous savez au moins que le thread ne peut pas se trouver dans du code non géré ou sortir d'une section critique en laissant un verrou suspendu dans un état acquis. Bien que cela soit moins invasif que Thread.Abort
Je déconseille toujours son utilisation car il n'est pas évident de savoir quels appels y répondent et de nombreux développeurs ne seront pas familiers avec ses nuances.
2 votes
Pensez-y de cette façon : voulez-vous vraiment que votre processus de longue durée s'arrête à n'importe quel moment arbitraire alors qu'il aurait pu être au milieu d'un traitement modifiant l'état ? La raison pour laquelle vous devez ajouter des vérifications au milieu du code (pas nécessairement dans une boucle, bien que cela soit courant) est que vous seul, en tant que développeur, connaissez suffisamment votre code pour savoir quand il est sûr de s'arrêter. Si le thread se bloque en attendant des signaux de synchronisation, consultez la méthode Interrupt() mentionnée dans les réponses.
0 votes
Je ne nie pas qu'il ne devrait pas être géré par l'utilisateur (moi), mais il me semblait simplement que c'était beaucoup de code superflu. Ce serait bien de mettre en place quelque chose comme la façon dont un try, catch s'exécute, en surveillant constamment, dès qu'un drapeau est faux, puis peut-être
return;
de la fonction. Je ne sais pas.0 votes
Il s'avère que ce que je voulais, c'était
Thread.Interrupt
. Bien que je pense que je préfère une vérification du drapeau bool, du moins pour l'instant, afin de garantir absolument l'intégrité de l'état de mon application.