multi thread et accés via un controleur

GGGG Membre
20:51 modifié dans API AppKit #1
Bonsoir,
je cherche à  voir comment je peux gérer des NSthread.

Si dans une classe, j'utilise des NSthread qui sont détachés du thread principal, et que je dois les abandonner depuis ma classe contrôleur, comment puis acceder à  ces nsthread depuis ma classe contrôleur.
Et le cas échéant, comment  les arrêter ?

Depuis ma classe qui détache les nsthreads, je peux connaitre le nombre de threads en cours par :
<br />[[[NSThread currentThread] threadDictionnary] count];<br />


Mais comment connaitre leur adresse et les arrêter depuis la classe contrôleur (de manière propre ;)).
«1

Réponses

  • schlumschlum Membre
    20:51 modifié #2
    On ne peut pas. On arrête un thread qu'à  l'intérieur de lui même. Et encore, il faut faire vachement gaffe à  la mémoire gérée par le thread si on veut l'arrêter en cours de traitement.
  • GGGG Membre
    20:51 modifié #3
    On ne peut pas récupérer les threads courants via la NSAppleEventManagerHandlingStack ?

    On pourrait pas faire un
    <br />(void)resumeWithSuspensionID:(NSAppleEventManagerSuspensionID)suspensionID<br />
    

    ?


    Autrement comment récupérer les NSAppleEventManagerSuspensionID via NSAppleEventManagerHandlingStack (c'est une pile il doit contenir ces classes NSAppleEventManagerSuspensionID) non ?

    Ce sont des questions naà¯ves mais j'aimerai comprendre pourquoi on ne pourrait pas récupérer des informations sur ces threads de manière propre.
  • schlumschlum Membre
    20:51 modifié #4
    J'avoue que j'ai du mal à  saisir le rapport entre les threads et les AppleEvents...
    Mais si tu arrives à  tuer proprement un thread de l'extérieur, ça m'intéresse aussi  :P
  • GGGG Membre
    20:51 modifié #5
    pardonnes moi, en gros je regarde les docs apple et ce que me sort mon code.
    Concrètement, lorsque j'affiche le contenu du dictionnaire du thread courant il me sort qu'il est de type NSAppleEventManagerHandlingStack.
    Donc je suis allé chercher dans la doc ce que c'était (rien dans la doc officielle, mais il y existe les NSAppleEventManager), et il existe une méthode qui permet d'arrêter ces events resumeWithSuspensionID.
    Je me dis (peut être à  tort), que si je pouvais récupérer ces NSAppleEventManagerSuspensionID par le biais de la pile, je pourrai tuer proprement les threads.
    Voila c'est tout, je suis en état de recherche actuellement ;).

    Je prospecte encore et je vous tiens au jus :p.
  • schlumschlum Membre
    20:51 modifié #6
    Les dictionnaires des threads (je connaissais même pas...) sont une bidouille d'Apple pour gérer je ne sais quoi...
    Je crois pas que ça ait à  voir avec le fonctionnement inhérent aux threads.
    Regarde du côté de pthread ("man pthread"), il y a une fonction (pthread_cancel) qui permet de tuer un thread de l'extérieur, mais le gros problème, c'est la mémoire ! Il faut à  tout prix éviter les leaks.
  • GGGG Membre
    20:51 modifié #7
    Merci du conseil.
    Mais comment fais tu pour connaitre les adresses des nsthreads lancés détachés du nsthread principal ? (je parle au sein de la classe où ils sont été lancés).
  • schlumschlum Membre
    20:51 modifié #8
    dans 1191494764:

    Merci du conseil.
    Mais comment fais tu pour connaitre les adresses des nsthreads lancés détachés du nsthread principal ? (je parle au sein de la classe où ils sont été lancés).


    Avec le wrapper NSThread qu'Apple a créé autour des fonctions de "phtread", c'est impossible !
    Il faut que les threads l'envoient au thread principal via une variable partagée ([NSThread currentThread])

    Mais attention, l'adresse du NSThread, n'est pas l'adresse du thread handler qu'on peut utiliser avec pthread_cancel !
  • GGGG Membre
    20:51 modifié #9
    mais il y a un fork quelque part, donc le wrapper NSThread connait ces adresses ?
    Comment peut on accéder à  ces threads via le NSthread currentthread ?
  • schlumschlum Membre
    octobre 2007 modifié #10
    dans 1191499831:

    mais il y a un fork quelque part, donc le wrapper NSThread connait ces adresses ?
    Comment peut on accéder à  ces threads via le NSthread currentthread ?


    Tutut, il n'y a pas de fork, un thread est au sein du même processus, et il partage les mêmes variables !
    Pour ce qui est d'accéder à  l'adresse du "pthread_t", c'est encapsulé dans leur classe NSThread, c'est impossible.
    Et quand bien même tu pourrais, tu ferais comment pour être sûr qu'il n'y a pas de leak en envoyant pthread_cancel ?

    Regarde ce sujet, beaucoup de grosses pointures en programmation de Macbidouille y ont participé  :P
    http://forum.macbidouille.com/index.php?showtopic=154181&hl=NSThread
  • AliGatorAliGator Membre, Modérateur
    20:51 modifié #11
    Ce que je te conseille de faire, et ce qu'on fait habituellement avec les threads en fait, c'est prévoir une condition de sortie (un test sur une variable par exemple) dans ton thread. Donc dans le thread tu testes si la variable par exemple bMustExit est à  true, et si c'est le cas alors il sort du thread.

    En général par exemple pour un thread qui est sensé faire une action en boucle, dans la méthode que l'on fait executer par le thread (le selecteur qu'on indique dans le detachThread), on met une RunLoop, du genre boucle while, et la condition de sortie de la boucle est la condition qui te permet de sortir du thread, donc de l'arrêter.

    Si c'est un thread qui ne fait pas un truc en boucle mais juste une action longue que tu veux paralléliser, il suffit de mettre des tests à  divers endroits de ton code (qui en général, long comme il est dans ce cas, est divisé en étapes de toute façon). Donc en gros tu crées des points de sortie.
    To terminate a thread, you should design your thread code to exit gracefully from its run loop. The simplest way to exit a run loop is to put your run loop call inside a while loop and put the run loop into "one-shot" mode. In this mode, the run loop processes only one event before returning control to your code. Your while loop can then check a status flag to determine whether it should exit. The more forceful way to terminate a thread is to kill it outright. This not only exits the run loop but also prevents any further code from being executed on the thread.

    Après pour signaler au thread que tu veux sortir, donc pour modifier la valeur de la variable que tu testes pour savoir si tu dois sortir ou pas, il y a plusieurs solutions : soit une variable partagée entre ton thread et le thread principal, soit utiliser les systèmes de communication inter-thread pour envoyer un message au thread lui demandant de sortir dès que possible (donc de mettre son flag bMustExit à  YES en gros)... Ceci dit dans ce genre de cas (variable partagée entre threads) il faut absolument protéger les accès à  ces variables (via des @synchronized par exemple, ou même mieux les NSLock) pour éviter que tu lises la variable pendant que ton thread principal la modifie par exemple. (c'est pour ça qu'une gestion par envoi de messages, laissant le thread modifier lui-même la variable que tu testes pour sortir de ta RunLoop, est plus sympa que d'utiliser une variable partagée accessible par plusieurs threads)

    Communication inter-threads avec les distributed objects : comment gérer les RunLoops et permettre au thread principal de demander à  un autre thread de s'arrêter
  • schlumschlum Membre
    20:51 modifié #12
    J'ai trouvé ces fonctions non documentées avec class-dump :

    - (id)runLoop;<br />- (void)setRunLoop:(id)fp8;
    

    Apparemment on peut faire joujou avec la runLoop du thread (s'il en a une !)... Après, faut voir ce qu'on peut en faire.
  • schlumschlum Membre
    octobre 2007 modifié #13
    Ah, aussi, il existe une fonction "pthread_self" qui renvoie le thread courant...
    Mais je déconseille vraiment de récupérer cette valeur et d'envoyer un pthread_cancel ! (rapport à  la mémoire, et à  la gestion multithreadée de Cocoa)

    La bibliothèque POSIX pthread de gestion de threads est basée sur la bibliothèque du noyau (mach) il me semble, et NSThread est basé sur la bibliothèque POSIX pthread...

    Si tu veux travailler directement avec les threads du noyau -> <mach/mach.h> (/usr/include/mach/i386/thread_act.h ou /usr/include/mach/ppc/thread_act.h)
  • hcyranohcyrano Membre
    20:51 modifié #14
    aligator a donné la solution :

    un objet partagé entre le controleur et le thread

    c'est aussi "simple" que ca

    ou je n'ai pas compri la question :-\\
  • schlumschlum Membre
    20:51 modifié #15
    dans 1191505886:

    aligator a donné la solution :

    un objet partagé entre le controleur et le thread

    c'est aussi "simple" que ca

    ou je n'ai pas compri la question :-\\


    Oui et non... C'est une solution (celle que j'avais finalement gardée n'en ayant pas trouvé de meilleure), mais elle n'est pas idéale.
    Pour qu'elle le soit, il faudrait un test à  chaque instruction du thread.

    C'est là  que le C++ a un gros avantage sur l'Objective-C : on peut définir des objets qui gèrent la mémoire, et dont le destructeur sera appelé quoi qu'il arrive (exception, fin de thread...).
  • hcyranohcyrano Membre
    octobre 2007 modifié #16
    ouarfff, un controle aux endroits strategiques est suffisant, et avant de demarer ton nouveau thread attendre que le thread se termine gentiment (on ne tue pas un/une thread on fait en sorte qu'il (elle) remonte (tres rapidement) ses appels jusqu'au point d'entrée (generalement run())

    join() (par exemple) ou a travers l'objet partagé

    non?
  • AliGatorAliGator Membre, Modérateur
    20:51 modifié #17
    dans 1191503804:
    Ah, aussi, il existe une fonction "pthread_self" qui renvoie le thread courant...
    Mais je déconseille vraiment de récupérer cette valeur et d'envoyer un pthread_cancel ! (rapport à  la mémoire, et à  la gestion multithreadée de Cocoa)

    La bibliothèque POSIX pthread de gestion de threads est basée sur la bibliothèque du noyau (mach) il me semble, et NSThread est basé sur la bibliothèque POSIX pthread...
    Oui on va sur une pente glissante à  mon avis de ce côté là  comme tu le mentionnes, schlum. NSThread n'est pas qu'un bête wrappeur sur les pthreads, certes il est basé dessus mais il prépare aussi l'application pour la passer en mode multithreadée et autres astuces que ne gère pas les pthread automatiquement... voir la doc

    De toute façon tout est indiqué dans la doc Apple : les différentes façons d'utiliser des threads. Et à  mon avis le plus simple est quand même d'utiliser le système de threads Cocoa (NSThreads) que de descendre au niveau des pthreads POSIX et de gérer tout soi-même.

    Et pour le coup de la runLoop, voir encore la doc Apple et "Configuring Your Run Loop"...
  • schlumschlum Membre
    octobre 2007 modifié #18
    dans 1191506526:

    ouarfff, un controle aux endroits strategiques est suffisant, et avant de demarer ton nouveau thread attendre que le thread se termine gentiment (on ne tue pas un/une thread on fait en sorte qu'il (elle) remonte (tres rapidement) ses appels jusqu'au point d'entrée (generalement run())


    Si, on peut tuer un thread... "pthread_cancel" est là  pour ça. Le vrai problème c'est la mémoire gérée ! ("pthread_cleanup_pop" et "pthread_cleanup_push" dans la bibliothèque POSIX)
  • schlumschlum Membre
    20:51 modifié #19
    dans 1191507725:

    Oui on va sur une pente glissante à  mon avis de ce côté là  comme tu le mentionnes, schlum. NSThread n'est pas qu'un bête wrappeur sur les pthreads, certes il est basé dessus mais il prépare aussi l'application pour la passer en mode multithreadée et autres astuces que ne gère pas les pthread automatiquement... voir la doc


    C'est pour ça que je disais que c'était déconseillé à  cause de la gestion multithreadée de Cocoa... Il y a plusieurs choses à  faire quand on veut gérer ses threads directement avec la bibliothèque POSIX avec Cocoa !
  • hcyranohcyrano Membre
    20:51 modifié #20
    quand je disais "on ne tue pas un thread" c'etait pour rester "propre" et pas faire le boulot comme un "cochon" :)
  • schlumschlum Membre
    octobre 2007 modifié #21
    dans 1191508935:

    quand je disais "on ne tue pas un thread" c'etait pour rester "propre" et pas faire le boulot comme un "cochon" :)


    Mais "pthread_cancel" est très propre ! C'est juste dommage qu'ils n'aient pas implémenté ça dans NSThread...

    En gros, à  chaque fois qu'on alloue des ressources, on fait un "pthread_cleanup_push" avec un pointeur de fonction censé relâcher ces ressources, et quand on fait "pthread_cancel", tous les "pthread_cleanup_pop" sont appelés, et le thread s'arrête très proprement.

    C'est LE truc qui manque à  leur wrapper NSThread (parce que bon, les conditions de sortie à  tous les endroits stratégique, c'est tout sauf léger !)
  • hcyranohcyrano Membre
    20:51 modifié #22
    dans 1191509753:

    C'est LE truc qui manque à  leur wrapper NSThread (parce que bon, les conditions de sortie à  tous les endroits stratégique, c'est tout sauf léger !)


    bon, disons que dans mon application cela ne me pose pas de probleme, traitement interrompu par une horloge (temps epuisé) ou par le controleur (mauvaise hypothese de depart).
  • GGGG Membre
    20:51 modifié #23
    Petite question :p,
    l'implémentation de sa propre runLoop permet juste de communiquer avec les thread s distribués ?
    En gros en implémentant cette méthode, on peut à  tout moment arrêter ces threads via une boucle loop runMode:beforeDate: ?
  • schlumschlum Membre
    octobre 2007 modifié #24
    dans 1191510834:

    dans 1191509753:

    C'est LE truc qui manque à  leur wrapper NSThread (parce que bon, les conditions de sortie à  tous les endroits stratégique, c'est tout sauf léger !)


    bon, disons que dans mon application cela ne me pose pas de probleme, traitement interrompu par une horloge (temps epuisé) ou par le controleur (mauvaise hypothese de depart).


    Bien sûr que c'est une solution valable  ;)
    Je dis juste qu'il pourrait y avoir mieux !
    NSThread n'a pas toute la flexibilité de la bibliothèque POSIX...


    D'ailleurs, ils le disent texto dans leur doc :

    Mac OS X includes several APIs for creating application-level threads. Behaviorally, threads created with one API are essentially identical to threads created with other APIs. Choosing an API for your application is often determined by the type of application (Carbon, Cocoa, Darwin) that you are creating, but it can also involve trade-offs between complexity and performance. For example, Cocoa threads are simple to use but offer less flexibility and performance than POSIX threads.


    The underlying implementation of NSThread is based on POSIX threads. If you need the improved performance and flexibility offered by POSIX threads, you can use them instead of NSThread in your application if you choose. If you still intend to use Cocoa, though, you first put your application into “multithreaded mode” before creating any POSIX threads.
  • schlumschlum Membre
    20:51 modifié #25
    dans 1191510911:

    Petite question :p,
    l'implémentation de sa propre runLoop permet juste de communiquer avec les thread s distribués ?
    En gros en implémentant cette méthode, on peut à  tout moment arrêter ces threads via une boucle loop runMode:beforeDate: ?



    Moi je me méfierais beaucoup de ce genre de considérations... Je ne sais pas si on peut le faire, mais ça me semble assez dangereux d'essayer en tout cas (surtout que c'est du non documenté !)
  • BruBru Membre
    20:51 modifié #26
    En reprenant les explications de Schlum, il est peut-être plus facile qu'on le croit de simuler les trucs "pthread_cleanup_push" and co.
    2 solutions sont peut-être envisageables : le releasePool et les NSZones.

    Dans le premier cas, il s'agit de contrôler la destruction d'un releasePool après un pthread_cancel(), moyennant de devoir faire attention à  ce que tous les objets crées dans le thread soient bien placés dans ce pool :
    <br />// variables globales<br />NSAutoreleasePool *threadPool=nil;<br />pthread_t thread;<br /><br />{<br />&nbsp;  [NSThread detachNewThreadSelector:uneMethode];<br /><br />&nbsp;  // ...<br /><br />&nbsp;  pthread_cancel(thread);<br />&nbsp;  if (threadPool) [threadPool release];<br />}<br /><br />- (void)uneMethode<br />{<br />&nbsp;  threadPool=[[NSAutoreleasePool alloc] init];<br />&nbsp;  thread=pthread_self();<br /><br />&nbsp;  // ...<br /><br />&nbsp;  [threadPool release];<br />&nbsp;  threadPool=nil;<br />&nbsp;  [NSThread exit]<br />}
    



    La seconde méthode repose sur les NSZones.
    Le principe est identique au releasePool (création d'un NSZone dont le pointeur est en globale, puis destruction de ce NSZone après le pthread_cancel). Dans ce cas, les objets doivent être créés via un allocWithZone:. L'inconvénient de cette dernière méthode est que la destruction du NSZone ne fait pas d'appel aux release des objets qui y sont contenus, ce qui peut être facheux pour certaines classes.

    .
  • schlumschlum Membre
    octobre 2007 modifié #27
    Il y a un problème avec ça qui a été très longuement discuté dans le sujet MB dont j'ai donné le lien... C'est qu'un  NSAutoReleasePool doit être créé et détruit dans le même contexte, sinon ça foire. (cf. ici)

    Là  tu le crées dans un thread secondaire, et tu le détruits dans un thread principal ; j'avais testé, c'est le leak assuré  :-\\


    Mais je suis content que la discussion reprenne ici, ça met un peu de sang neuf au problème  :)
  • schlumschlum Membre
    20:51 modifié #28
    Apparemment (d'après mes tests, mais je n'arrive pas à  trouver l'info ailleurs), le noyau mach ne supporte pas PTHREAD_CANCEL_ASYNCHRONOUS... C'est la loose quand même...
    Ca expliquerait en tout cas que ça ne soit pas implémenté dans le wrapper NSThread
  • GGGG Membre
    20:51 modifié #29
    grosso modo, je n'ai pour le moment pas de moyen simple pour arrêter les threads déjà  lancés ?

    Mon idée initiale était de si jamais je dois relancer la classe qui lance ces threads détachés du principal, alors que les précédents n'ont pas terminés, j'arrête les précédents de manière propre.

    En C++ avec Boost on peut faire du multi-thread via des templates, qui nous donnent accès à  ces threads, et donc de pouvoir les arrêter correctement, sans fuite mémoire. c'est quand même marrant, que cocoa permettent de lancer ces multi-threads mais ne nous donne pas les moyens de les contrôler.



  • schlumschlum Membre
    20:51 modifié #30
    Contrairement à  ce qu'on disait au dessus, gérer ses threads avec la bibliothèque POSIX n'est pas si compliqué que ça... Il faut juste lancer un thread vide avec "detachNewThreadSelector:toTarget:withObject:" au début de l'application pour la mettre en mode multi-threadée et après on fait ce qu'on veut avec la bibliothèque qu'on veut.

    Je suis en train de faire des tests dessus, mais je n'arrive pas à  faire fonctionner "PTHREAD_CANCEL_ASYNCHRONOUS"  >:(
    Par contre, "PTHREAD_CANCEL_DEFERRED" avec des "pthread_testcancel" dans le code fonctionne très bien.
  • hcyranohcyrano Membre
    20:51 modifié #31
    je viens de regarder la classe NSThread  :)

    piges pas, a quoi peut servir (de facon un peu serieuse) cette classe?
    a ton acces a l'implementation de cette classe? (curiosité)

    comme le dit Apple pour les application un peu plus "pointues" utiliser les thread posix

    ps : a quoi peu bien servir la methode currentThread qui retourne un NSThread* si on ne peut pas manipuler le thread a partir d'un pointeur
Connectez-vous ou Inscrivez-vous pour répondre.