Gui n'est pas thread safe :(

GGGG Membre
19:01 modifié dans API AppKit #1
Bonjour à  tous,

voila dans une de mes application, je charge (et oui encore :)), des images en utilisant des NSThreads. Donc je détache le chargement de ces images ([NSThread detach....]).
Tout se passe bien tant que je n'ai pas trop d'images. dès que je commence à  charger plus de 700 images, l'interface ne se synchronise plus et part en vrille.

J'ai regardé dans la doc, et il conseille d'utiliser NSConditionLock.
Mais voila, comment faire d'un point de vue conceptuel.
Sachant que le selector (méthode), qui charge les images en mémoire et dans le même fichier controleur que la NSTableView (le NSTread "voit" la NSTableView).

Avez vous un petit exemple d'utilisation de cette classe NSConditionLock ?

Jerome

Réponses

  • AliGatorAliGator Membre, Modérateur
    19:01 modifié #2
    Attention pour tout ce qui est mise à  jour de l'interface, il faut toujours que cela soit fait dans le mainThread !
    Il faut pour cela effectuer les dessins dans le thread principal, quitte à  faire des appels à  [tt]performSelectorOnMainThread:[/tt] qui est fait pour ça justement. (On en a déjà  parlé sur les forums, tu peux faire une recherche si tu veux plus de détails)
  • GGGG Membre
    19:01 modifié #3
    C'est justement ce que je fais :
    - lancement d'une recherche via API de spotlight, avec un observer qui regarde à  interval de temps régulier et lance updateData.
    - updateData :
    pour chaque entrée trouvée par l'API Spotlight, lance un [NSThread detach....@selector(loadThreaded)]
    - la méthode loadThreaded est imbriquée dans un NSAutoreleasePool et envoie dans cette méthode un [NSThread performOnMainThread] pour le reloadData de la NSTableView, mais tant qu'il n'y a pas beaucoup d'objet ça va, dés qu'il y a un grand nombre ça crache.


  • Philippe49Philippe49 Membre
    janvier 2008 modifié #4
  • GGGG Membre
    19:01 modifié #5
    C'est accepté pour un projet qui doit s'executer sur une 10.4 ?

  • Philippe49Philippe49 Membre
    19:01 modifié #6
    Bonne question, essaye tu auras la réponse à  la compilation
  • MalaMala Membre, Modérateur
    19:01 modifié #7
    Tu lances autant de threads que d'images? Si oui, j'aurais tendance à  étudier les pistes suivante:
    - Quelle est la limite du nombre de thread pouvant être lancés en parallèle en même temps pour un processus donné?
    - La mémoire ne grimpe-t-elle pas trop (swap à  mort de la machine entraà®nant le plantage)?

    Pour ce qui est du @synchronized ce n'est pas spécifique à  Obj-C 2.0 donc pas de souci. Maintenant cela sert surtout lors d'accès critiques à  des données partagées. Si c'était un problème de partage de ressources cela aurait tendance à  planter de temps en temps quelque soit le nombre d'image.

  • schlumschlum Membre
    janvier 2008 modifié #8
    dans 1200491189:

    C'est justement ce que je fais :
    - lancement d'une recherche via API de spotlight, avec un observer qui regarde à  interval de temps régulier et lance updateData.
    - updateData :
    pour chaque entrée trouvée par l'API Spotlight, lance un [NSThread detach....@selector(loadThreaded)]
    - la méthode loadThreaded est imbriquée dans un NSAutoreleasePool et envoie dans cette méthode un [NSThread performOnMainThread] pour le reloadData de la NSTableView, mais tant qu'il n'y a pas beaucoup d'objet ça va, dés qu'il y a un grand nombre ça crache.


    Si c'est ce que tu fais et que tous les rafraà®chissements graphiques se font dans le thread principal, pas besoin de lock... Il faut chercher ailleurs !
    Maintenant, s'il s'agit de "reloadData" et que pendant l'exécution de "reloadData" tu t'amuses à  modifier les données du dataSource, c'est sûr ça risque de poser problème, donc à  priori il faudrait modifier les données du dataSource dans le thread principal aussi !
  • GGGG Membre
    19:01 modifié #9
    merci Schlum,
    je n'avais pas vu ça comme ça.
    Je vais retester en modifant les valeurs du dataSource dans le Thread Principal.

    Je vous tiens au courant;).
  • GGGG Membre
    19:01 modifié #10
    Voila je viens de refaire le code.
    Le datasource de la NSTableView est modifié uniquement dans le Thread principal, mais encore une fois même effet.
    ça plante dés que l'on dépasse les 1600 NSImage en mémoire (d'une taille raisonnable 50x50 ;)).

    Et pourtant je regarde s'il y  des fuites de mémoire, rien de ce côté.


  • GGGG Membre
    19:01 modifié #11
    En fait il fait comme s'il avait un buffer, car les images s'affichent à  chaque lancement de la méthode signalée dans le NSNotification.
    Est ce du au lancement de spotlight  ?
    <br />[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(finishQuery:)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name:(NSString*)kMDQueryDidFinishNotification object:(id)_query];<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateData:)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name:(NSString*)kMDQueryProgressNotification object:(id)_query];<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateData:)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name:(NSString*)kMDQueryDidUpdateNotification object:(id)_query];<br /><br />
    

    Il n'est pas lancé en batch.
    <br />MDQueryExecute(_query, kMDQueryWantsUpdates);<br />
    

  • 19:01 modifié #12
    Pour ceux qui ne suivraient pas c'est la suite de http://www.objective-cocoa.org/forum/index.php/topic,2523.msg24618.html#msg24618 évidemment resté sans réponse.

    Je vois toujours pas pourquoi se compliquer avec un Thread en plus et même je vois toujours pas où serait le problème. En fait, je suis un peu de mauvais poil ce matin, dis moi GG, quel âge as tu ?

  • GGGG Membre
    19:01 modifié #13
    Comment dois je le prendre ?
  • schlumschlum Membre
    19:01 modifié #14
    Comme quelqu'un qu'on a aidé sur un sujet et qui n'a pas renvoyé de feedback  ;)
  • GGGG Membre
    19:01 modifié #15
    Bien, Supermic, je te présente mes excuses pour ne pas avoir donner suite à  ta réponse.
    Je trouve que tu le prends mal assez vite, car de n'a pas pris en compte dans quel état d'esprit j'étais.

    Le fait d'utiliser des threads a de nombreux avantages :
    - éviter que l'interface gèle le temps du calcul (au contraire de beaucoup d'application windows par exemple)
    - et donc garder la main sur l'interface et permettre d'effectuer d'autres tâches avec cette interface.
    - de mieux les connaà®tre (NSThread)
    - de permettre une certaines optimisation en fonction de la machine sur laquelle tourne l'application (présente maintenant des Dua-core, multiprocs...)

    Donc si je n'ai pas répondu à  ta réponse, je m'excuse encore, bien que je trouve cela bien surproportionner.

    Et excusez moi pour ces différents posts...
  • janvier 2008 modifié #16
    Merci, je sais très bien à  quoi servent les threads mais quand on peut s'en passer c'est mieux non ? La preuve !

    Encore une fois tu ne dis absolument pas ce que tu veux faire et c'était là  ce que je cherchais à  te faire dire par mon petit exemple, je m'attendais à  une réponse du genre c'est "tout à  fait ce que je cherche à  faire" (sur le principe) ou "pas du tout... de quoi tu me parles" mais rien de tout ça... et aujourd'hui tout le code que tu postes vient de mon exemple... et c'est assez amusant non ?

    Alors, as-tu réellement besoin de 1600 images en mémoire ? Non, même pour un browser de dossiers c'est trop de trop. Tu veux dessiner ces images ? Tu veux faire quoi de ces milliers d'images ?

    Le NSTableView permet de déssiner l'image uniquement lorsque la ligne est affichée, ce n'est pas plus simple qu'un thread ? C'est largement rapide (chargement de l'icône/accès disque) et avec un petit système de cache c'est quasi-parfait.

    Alors, d'accord / pas d'accord ? Tu n'as jamais indiqué ce qu'étaient ces images ? Des icônes de fichiers que je le supputais ? Du caca ? Popo ? Pipi ?

    Si avais une idée de la journée que je viens de passer (et mon avatar en montre une partie) tu saurais que je ne suis absolument pas vexer tellement je m'en moque au fond mais il me semble qu'il y a quelques petits principes à  respecter.
  • GGGG Membre
    19:01 modifié #17
    Apparemment tu m'as pris en grippe et je crois que rien ne pourra changer ton opinion.
    Je suis désolé que tu le prennes comme ça car je ne t'ai pas agressé, du moins je le pense.
    Concernant le code que j'ai écris dans ce poste ressemble à  celui que tu as écris.
    Bien d'accord, mais le code que j'ai écris date de plus vieux donc il n'y a pas de problème de copyright. ;)
    Si tu veux savoir d'où je me suis inspiré voici l'url :
    http://developer.apple.com/samplecode/SpotlightAPI/listing8.html

    Effectivement ton code n'apportait pas grand chose de neuf et mon seul tord a été de ne pas te le dire.

    Si je ne donne pas le but ultime de ce que je veux faire c'est pour vous garder une surprise.
    Je pensais sincèrement que je trouverai pas ce genre de comportement ici.

    Je ne vois réellement pas ce que je peux faire pour toi à  part m'excuser de ne pas d'avoir répondu.
    Si tu veux que je dégage tu peux le dire ouvertement, je dis sincèrement ce que je pense.

    Si tu regardes bien mes posts et les postes de la plupart des débutants, ils sont tous du même genre. J'en reviens au fait que je ne comprends vraiment pas ce que tu me reproches personnellement.

    Voila ne prends pas mal ce que je viens de dire mais je suis très surpris par ton agression envers moi, on ne se connaà®t pas, et je n'ai jamais été déplacé et je pense que je ne mérite pas un tel retour.

  • BruBru Membre
    19:01 modifié #18
    Heh, calmez-vous, les enfants !

    GG : plus tu donnes d'infos, et plus les gens (bénévoles, qui ont une famille, un boulot) pourront t'aider ou t'aiguiller. La programmation ne se résume pas à  "pisser du code", elle démarre par un cahier des charges qui montre les tenants et aboutissants du projet. C'est à  partie de cette structure solide que se construit le code.
    Nous montrer des bouts de codes, et dire que ça ne marche pas n'est pas suffisant.
    Dans aucun de tes messages tu ne postes les messages d'erreurs ou le résultat du crash-log, ce qui pourrait au minimum nous aiguiller sur le type d'erreur, et donc sur "éventuellement" sur son origine.

    Supermic : même si dans le principe, tu n'as pas tord, ce n'est pas parce que tu "es de mauvais poil ce matin" que ça doit justifier ta réaction. Je ne sais pas ce que tu fais. Moi, j'aimerais bien passer mes journées à  grimper sur des cailloux plutôt qu'à  me farcir des rédactions de cahier des charges et de subir des poussées de réunionnite aiguë.

    Voilà . Fin des hostilités.
    A la suite.

    .

  • AliGatorAliGator Membre, Modérateur
    19:01 modifié #19
    [EDIT]Zut grillé par Bru[/EDIT]

    J'avoue que je suis tout aussi surpris que toi GG par la réaction de SuperMic...Mais bon j'ai peut-être loupé un épisode
    Quoi qu'il en soit... que tout le monde se calme :P Ne cherchons pas qui est le plus en tord ou quoi, et reprenons posément depuis le début.


    dans 1200682559:
    Merci, je sais très bien à  quoi servent les threads mais quand on peut s'en passer c'est mieux non ?
    Je ne te suis pas trop SuperMic, moi j'aurais pas dit ça aussi radicalement. Au contraire je trouve qu'il y a parfois pas mal d'applis qui ne sont pas multithreadées alors qu'elles gagneraient à  l'être, surtout pour les tâches de chargement ou phases de calculs... Bien sûr on n'est d'accord, si une appli n'a vraiment pas besoin de plusieurs threads y'a aucun intérêt à  en mettre uniquement pour le principe, ça serait stupide.
    Mais si dans un programme il peut y avoir une utilité d'en avoir un (chargement d'un objet qui peut prendre du temps par ex), ce serait bête de s'en priver, et c'est aussi l'occasion d'un bon exercice pour comprendre comment ça marche et découvrir les subtilités et astuces auxquelles il faut faire attention, en s'intéressant au sujet des threads.

    C'est d'ailleurs ce qu'a expliqué GG dans son post, s'il est parti pour le faire avec des Threads c'est d'une part parce que ça peut se justifier (chargement d'images risquant d'être long et pouvant être parallélisé) et d'autre part parce que c'est une bonne occasion de creuser le sujet

    lors, as-tu réellement besoin de 1600 images en mémoire ? Non, même pour un browser de dossiers c'est trop de trop. Tu veux dessiner ces images ? Tu veux faire quoi de ces milliers d'images ?
    Là  par contre je suis d'accord... Autant au début je pensais que GG ne souhaitait que lister les images (opération de création du listing par un thread, et ne charger les images une par une qu'à  la demande, sans utiliser de thread du coup), mais d'après ce que j'en lis ensuite c'est bien les images qu'il veut charger en parallèle, sans doute pour les afficher toutes ensemble ou pour "prendre de l'avance" en chargeant quelques images d'avance.

    Après, je suis bien d'accord qu'il ne faut pas pour autant charger un millier d'images, l'emprunte mémoire laissée par l'appli étant alors disproportionnée par rapport aux besoins. La seule raison pour laquelle cela pourrait être justifiée, c'est... pour faire des tests de charge de son appli, pour vérifier qu'il a bien implémenté ses threads et qu'ils sont bien synchronisés (pas d'accès concurrenciels aux données). A part ça, je vois pas.
    Mais de toute façon, un bon millier d'images chargées à  la fois, vu l'utilisation que ça doit prendre en mémoire, c'est à  mon avis plus un "Out of Memory" qu'il risque d'obtenir alors, qui pourrait alors résulter en un crash de l'appli, qu'un crash dû au fait qu'il a plusieurs threads potentiellement concurrentiels...


    Voilà , c'était my two cents de la vision de ce post sans avoir pour autant étudié plus que ça la question ni regardé les codes que vous citez dans ce fil de discussion... ;D
Connectez-vous ou Inscrivez-vous pour répondre.