Annulation de Thread...

07:41 modifié dans API AppKit #1
Bonjour,

Voilà , je cherche à  annuler un thread en cours d'execution.
Je m'explique :
Mon application possède un tableau. Selon la selection dans ce tableau, on obtient des informations affichées dans des textFields. (iFreete pour ceux qui connaissent).
Lorsque la selection d'une ligne change (donc tableViewSelectionDidChange), j'envoi une méthode qui complète les champs selon l'objet sélectionné.
Dans cette méthode, je crée un nouveau Thread qui a pour but de récupérer un texte situé sur le net (initWithContentsOfURL:encoding:error:)

Malheureusement, lorsque je change la selection de la tableView très rapidement et plusieurs fois de suite et que je m'arrête au bout d'un moment, on voit le textField (celui du thread qui récupère le texte sur le net), qui change son contenu ... jusqu'à  l'objet sélectionné.

J'aimerai donc pouvoir arrêter un thread en cours. j'ai déjà  essayé [NSThread exit]; qui, d'après ce que j'ai compris, stoppe le thread en cours d'execution... Mais l'application plante.

Voilà  je suis un peu perdu...  :why?:

Réponses

  • Eddy58Eddy58 Membre
    07:41 modifié #2
    Peut-être qu'il faut locker une certaine partie du code...il nous le faudrait d'ailleurs ce code pour pouvoir t'en dire plus...;)
  • 07:41 modifié #3
    Voici :
    <br />- (void)updateFieldsWithFreeware:(NSDictionary *)freeware<br />{<br />&nbsp;  // Completion des fields<br />&nbsp;  [NSThread detachNewThreadSelector:@selector(showUserMark:) toTarget:self withObject:freeware];<br />}<br /><br />- (void)showUserMark:(id)freeware<br />{<br />	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];<br />	// ONLINE MARK<br />	NSString*	_userMarkString;<br /><br />	[usersMarkField setStringValue:NSLocalizedString(@&quot;Loading...&quot;,nil)];<br /><br />	NSString*	_URLString = [[NSString stringWithFormat:@&quot;%@%@%@&quot;, MEDIUMMARKURL, [freeware objectForKey:@&quot;name&quot;], MEDIUMMARKURLEND] stringByAddingPercentEscapesUsingEncoding:NSISOLatin1StringEncoding];<br />	<br />	if([NSString instancesRespondToSelector:@selector(initWithContentsOfURL:encoding:error:)])<br />		_userMarkString = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:_URLString] encoding:NSUTF8StringEncoding error:nil];<br />	else<br />		_userMarkString = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:_URLString]];<br />		<br />	if(![_userMarkString isEqual:@&quot;&quot;])<br />		[usersMarkField setStringValue:[tableView checkMark:_userMarkString forImage:NO]];<br />	else<br />		[usersMarkField setStringValue:[tableView checkMark:nil forImage:NO]];<br /><br /><br />	[pool release];<br />}
    
  • Eddy58Eddy58 Membre
    mars 2006 modifié #4
    En effet, à  chaque changement de sélection dans ta tableview, un new thread est créé, et chaque thread agit sur les mêmes éléments de l'AppKit, donc forcément ça va pas. Il faut locker le code critique, donc celui qui modifie tes controles. Quand un thread va prendre le lock, cela empêchera les autres threads d'accéder au code critique jusqu'à  ce ce que le thread possédant le lock le libère une fois que l'exécution du code critique est terminée. :)
    Voilà , sinon deux ou trois autres commentaires à  lire dans le code ci-dessous sur des choses qui ne me plaisent pas. Avec tout ça, ça devrait déjà  aller mieux je pense...;)

    A rajouter dans l'interfaçage :
    [tt]
    NSLock *showUserMarkLock;
    [/tt]
    <br /><br />-(void)dealloc<br />{<br />     [showUserMarkLock release];<br />     [super dealloc];<br />}<br /><br />-(void)awakeFromNib<br />{<br />     showUserMarkLock=[[NSLock alloc] init];<br />}<br /><br />- (void)updateFieldsWithFreeware:(NSDictionary *)freeware<br />{<br />   // Completion des fields<br />   [NSThread detachNewThreadSelector:@selector(showUserMark:) toTarget:self withObject:freeware];<br />}<br /><br />- (void)showUserMark:(id)freeware<br />{<br />     NSAutoreleasePool *pool;<br />     NSString *userMarkString;<br />     NSString *URLString;<br /><br />      if ([showUserMarkLock trylock]) // On essaie d&#39;obtenir le lock<br />      {<br />	   pool=[[NSAutoreleasePool alloc] init];<br />	   // ONLINE MARK<br /><br />	   [usersMarkField setStringValue:NSLocalizedString(@&quot;Loading...&quot;,nil)];<br /><br />	   URLString=[[NSString stringWithFormat:@&quot;%@%@%@&quot;, MEDIUMMARKURL, [freeware objectForKey:@&quot;name&quot;], MEDIUMMARKURLEND] stringByAddingPercentEscapesUsingEncoding:NSISOLatin1StringEncoding];<br />	<br />	   if([NSString instancesRespondToSelector:@selector(initWithContentsOfURL:encoding:error:)])<br />		   userMarkString=[[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:URLString] encoding:NSUTF8StringEncoding error:nil];<br />	      else<br />		   userMarkString=[[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:URLString]];<br />		<br />	   if(![userMarkString isEqualToString:@&quot;&quot;]) // isEqualToString plutot que isEqual, c&#39;est fait pour ça<br />		   [usersMarkField setStringValue:[tableView checkMark:userMarkString forImage:NO]];<br />	      else<br />		   [usersMarkField setStringValue:[tableView checkMark:nil forImage:NO]];<br /><br />          [userMarkString release]; // Ne pas oublier de releaser userMarkString !<br />          [pool release];<br />          [showUserMarkLock unlock]; // Libération du lock<br />     }<br />}<br />
    

  • 07:41 modifié #5
    Justement le truc c'est que je veux que si il change de sélection, il annule le thread qui était en cours pour passé à  celui qui va être demandé
  • Eddy58Eddy58 Membre
    07:41 modifié #6
    Le moment le plus propice pour faire l'exit se situe avant le detachNewThreadSelector. Est-ce ici que tu as essayé de le faire ?
    Sinon, avec le locking, tes threads en veille vont prendre le lock chacun leur tour, et leur exit se fera tout simplement à  la fin de ta méthode showUserMarck. Je trouve pas ça plus mal... :o  
  • 07:41 modifié #7
    Si je fais
    <br />- (void)updateFieldsWithFreeware:(NSDictionary *)freeware<br />{<br />if(thread)&nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; [NSThread exit];<br />	[NSThread detachNewThreadSelector:@selector(showUserMark:) toTarget:self withObject:freeware];<br />}<br />
    

    thread = YES au début de la méthode showUserMark: et deviens NO à  la fin de la méthode.
    Comme le chargement dure quand même quelques secondes, j'ai testé la condition, elle marche bien hein... sauf que quand il prend exit dans la gueule, mon application plante... :p

    J'ai aussi une autre méthode, qui récupère une icone. Là  c'est pareil, si je fais rien et que je laisse mon thread faire, il suffit de changer la sélection et là  on verra les icônes défiler jusqu'à  la dernière correspondante à  l'item sélectionné dans la table.

    :p
  • aranaudaranaud Membre
    07:41 modifié #8
    <br />[NSThread detachNewThreadSelector:@selector(showUserMark:) toTarget:self withObject:freeware];<br />
    

    A mon avis, cette ligne ne sert à  rien puisque le tread n'est plus actif.
  • 07:41 modifié #9
    Ben si justement,
    je désactive le thread pour le réactiver depuis le début  :o
  • aranaudaranaud Membre
    07:41 modifié #10
    dans 1143366200:

    - (void)updateFieldsWithFreeware:(NSDictionary *)freeware
    {
    if(thread)     
          [NSThread exit];    [glow=red,2,300]fin du tread en cours, donc arrêt de l'exécution de cette partie du code[/glow]
    [NSThread detachNewThreadSelector:@selector(showUserMark:) toTarget:self withObject:freeware];
    }

    Comment veux tu que la ligne en dessous puisse s'exécuter ?  :fouf):
  • 07:41 modifié #11
    dans 1143376620:

    Comment veux tu que la ligne en dessous puisse s'exécuter ?  :fouf):


    Il a raison sur la thériro. Il veut arrêter le thread puis lancer un NOUVEAU thread. Mais actuellement je pense qu'il fait un exit sur le thread principal au mieux.

    Il y a un gros problème de conception. Tu ne peux pas arrêter un thread comme ça. Déjà  que devient l'autorelease pool du thread dans ce cas ? : fuite de mémoire.

    C'est le thread lui même qui doit savoir quand s'arrêter et reprendre un autre téléchargement, déjà  dans ce cas il n'y aurait qu'un thread lancé et non plein de threads lancées/stoppés. Ca sous entend que le thread n'a pas de fonction bloquante mais est dans l'attente de la fin du chargement en cours, d'un eventuel arrêt ou un changement des données à  chargée.

    Je verrais bien un file d'attente avec tous les éléments à  charger, car pourquoi les chargers en partie alors qu'il pourront être redemandés par la suite. Tu charges tout ce que l'utilisateur demande et le garder en mémoire.

    Tu ajoutes dans un tableau les éléments que l'utilisateur veut charger et charge dans le thread tous ces éléments. Le tableau est commun au thread principal et secondaire. Pour un bonne réactivité charge le dernier élément dès que le chargement en cours est terminé, de cette facon les éléments demandés avant le dernier chargement seront chargé après celui-ci (en fond).
  • 07:41 modifié #12
    C'est pas bête sauf que comme je récupère un NSString en ligne, il peut changer à  tout moment. En effet, je récupère une "note" qui est la moyenne de toutes les notes postées par les utilisateurs du logiciel. Donc elle peut varier à  tout moment.

    Bon si c'est pas possible, tant pis. c'est juste chiant pour les petites connexions (56K :() Mais avec le cache (notemment pour ce qui est de l'image à  récupérer en ligne) c'est super. Et comme le NSString est vraiment léger (moins d'1Ko), c'est pas vraiment long.

    J'ai juste posé cette question histoire de savoir s'il y avait moyen d'améliorer mon application, mais si je fais que la pourrir...  :p

    Merci,
    Louka
  • aranaudaranaud Membre
    07:41 modifié #13
    Il y a peut-être une solution. Voir avec ce sujet : Peut-on envoyer une action d'un thread à  l'autre ?.
  • mars 2006 modifié #14
    Si les strings sont si petits pourquoi ne pas tous les charger d'un coup, au démarrage ou à  un autre moment.

    Les notes changent ok, mais je parlais d'un cache mémoire le temps que fonctionne ton applicaion. Ce n'est pas grave si la note varie de 0.1 et que l'utilisateur qui n'as pas relancé l'application ne la voit pas. Si ça varie de beaucoup c'est que tu n'as pas beaucoup de votes ce qui est un autre problème.

    Il vaut mieux que ton problème soit un peu lent sur un 56K (de toutes facon tout est lent pour eux) une bonne fois pour toute (au démarrage) que d'avoir un programme qui plante ou s'enmelle dans ses threads.

    Note aux démonteurs de forum, serait-il possible de réduire en deux la ligne qui défonce tout ? (Je suis sous Camino qui ne s'en sort pas)
  • mars 2006 modifié #15
    En complément à  ce que dit Supermic, j'ajouterais qu'une requete vers un server web est toujours accompagnée de données supplémentaires. Si pour une string qui fait 3 bytes, tu en envoies plusieurs centaines, tu économiseras globalement de la bande passante en faisant qu'une seule requete.
Connectez-vous ou Inscrivez-vous pour répondre.