L'approche consistant à utiliser l'héritage, le parent faisant office de contrôle pour le fil et les enfants mise en œuvre de les fonctions est une mauvaise idée en général. Les problèmes courants de cette approche proviennent de la construction et de la destruction :
-
si le thread est lancé à partir du constructeur du parent (contrôle), il peut commencer à fonctionner avant que le constructeur ne soit terminé et le thread peut appeler la fonction virtuelle avant que l'objet complet n'ait été entièrement construit.
-
si le fil est arrêté dans le destructeur du parent, alors au moment où le contrôle rejoint le fil, celui-ci exécute une méthode sur un objet qui n'existe plus.
Dans votre cas particulier, vous touchez le deuxième cas. Le programme commence à s'exécuter, et dans main
le deuxième fil est lancé. À ce moment-là, il y a une course entre le fil principal et le nouveau fil lancé, si le nouveau fil est plus rapide (peu probable, car le démarrage du fil est une opération coûteuse), il appellera la méthode membre Tick
qui sera envoyé à la commande finale. Fn::Tick
.
Mais si le thread principal est plus rapide, il sortira de la portée de l'option main
et il commencera la destruction de l'objet, il achèvera la destruction de l'objet. Fn
et pendant la construction de l Runnable
elle le fera join
le fil. Si le fil principal est assez rapide, il arrivera jusqu'au join
avant le second thread et attend là que le second thread appelle Tick
sur le maintenant le surrégime final qui est Runnable::Tick
. Notez que c'est Comportement indéfini et non garanti, puisque le second thread accède à un objet en cours de destruction.
En outre, il existe d'autres ordres possibles, comme par exemple, le deuxième thread pourrait envoyer des messages à Fn::Tick
avant que le thread principal ne commence la destruction, mais il se peut qu'il ne termine pas la fonction avant que le thread principal ne détruise les Fn
sous objet, dans ce cas, votre deuxième thread appellerait une fonction membre sur un objet mort.
Vous devriez plutôt suivre l'approche de la norme C++ : séparer les éléments contrôle de la logique pour construire complètement l'objet qui sera exécuté et le passer au thread pendant la construction. Notez que c'est le cas de la méthode Java Runnable
ce qui est recommandé par rapport à l'extension de la Thread
classe. Notez que, du point de vue de la conception, cette séparation est logique : la classe filetage gère l'exécution, et l'objet exécutable est le code à exécuter. Un thread n'est pas un téléscripteur mais plutôt ce qui contrôle l'exécution de la téléscripteur . Et dans votre code Runnable
n'est pas quelque chose qui peut être exécuté, mais plutôt quelque chose qui exécute d'autres objets qui en dérivent.