[Clos/Résolu] Utilisation de WaitThread

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

[Clos/Résolu] Utilisation de WaitThread

Message par boddhi »

Hello,

Je sollicite l'aide des connaisseurs en threads.
Ci-dessous, un (bout de, expurgé au mieux) code destiné à lister récursivement le contenu de plusieurs dossiers en faisant appel à un thread.

L'objectif du code complet, réalisé pour le compte d'une personne informatiquement bordélique, est :
• Dans un premier temps, de récupérer la liste de tous les dossiers et fichiers non système situés dans divers répertoires et les déplacer vers un dossier temporaire.
• Dans un deuxième temps, idem, lister les répertoires, sous-répertoires et fichiers d'un dossier cible choisi par l'utilisateur
• Dans un troisième temps, rechercher des doublons réels ou potentiels selon le nom, la taille et/ou la date de modif entre les fichiers de ces deux listes
• Et pour finir, permettre à l'utilisateur de supprimer les doublons qu'il souhaite

Pour cela, je souhaite utiliser un thread pour chacune de ces étapes pour permettre, entre autres, à l'utilisateur de pouvoir mettre en pause voire arrêter le processus en cours.
A cette fin, j'utilise une variable structurée créée pour contenir diverses infos sur le thread, son état, la liste des dossiers à scanner, etc...

Mon idée est de lancer, pour les trois premières étapes, des threads l'un après l'autre, en bloquant l'exécution du programme principal entre chacun d'eux par un WaitThred(). Voir procédure Pc_Traitement_BtFPLancer() lignes 340 et 343.

Et c'est là que le bât blesse ! WaitThread() n'a pas le comportement auquel je m'attendais.
En effet, l'instruction bloque tout ! Et le thread et le programme principal

Je m'y prends très vraisemblablement mal mais je ne comprends pas comment et où...

Ici, le bout de code :

Code : Tout sélectionner

EnableExplicit

; ╔═════════════════════════════════════════════════════════════════════════════╗
; ║ STRUCTURES - ENUMERATIONS - CONSTANTES - MACROS - MAPS - VARIABLES GLOBALES ║
; ╚═════════════════════════════════════════════════════════════════════════════╝
;{ ════  STRUCTURES           ════
;- ════  STRUCTURES           ════
Structure FICHIER
  Nom.s
  Chemin.s
  Taille.q
  DateModif.l
  Attributs.u
  FichierDeplace.a
EndStructure
Structure INFOSTHREAD
  IDThread.i
  NoThread.i
  Signal.i
  Pause.i
  Fin.i
EndStructure
Structure DONNEESTHREAD Extends INFOSTHREAD
  ; Data
  Fenetre.i
  NiveauRecursif.i
  CheminOriginel.s
  Chemin.s
  Filtre.s
  Initialisation.a
  List Dossiers.s()
EndStructure
;}
;{ ════  ENUMERATIONS         ════
;- ════  ENUMERATIONS ════
Runtime Enumeration Fenetres ;{
  #FEN_PRINCIPALE
EndEnumeration ;}
Runtime Enumeration Gadgets ;{ Gadgets généraux
  #GAD_FP_BG_DOSSIERS
  #GAD_FP_LS_DOSSIERSSOURCE
  #GAD_FP_LIB_DOSSIERCIBLE
  #GAD_FP_LIB_ACTION
  #GAD_FP_BG_INFOS
  #GAD_FP_LIB_DOSSIER
  #GAD_FP_LIB_NOMDOSSIER
  #GAD_FP_LIB_FICHIER
  #GAD_FP_LIB_NOMFICHIER
  #GAD_FP_BT_LANCER
  #GAD_FP_BT_QUITTER
EndEnumeration ;}
Enumeration XML ;{
  #ARBRE_XML
EndEnumeration ;}
Enumeration DialoguesXML ;{
  #XML_FENPRINCIPALE
EndEnumeration ;}
Enumeration #PB_Event_FirstCustomValue
  #EVENMTTHREAD_ANALYSETERMINEE
  #EVENMTTHREAD_ANALYSEANNULEE
  ;#MyEvent_ThreadGetFilesString
EndEnumeration
;}
;{ ════  MACROS               ════
; -════  MACROS               ════
Macro Mc_FormatageXML(ArgChaine)
  EscapeString(ArgChaine,#PB_String_EscapeXML)
EndMacro
Macro Mc_DeformatageXML(ArgChaine)
  UnescapeString(ArgChaine,#PB_String_EscapeXML)
EndMacro
;}
;{ ════  LISTES               ════
;- ════  LISTES               ════
Global NewList Fichiers.FICHIER()
;}
;-══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
; ╔═══════════════════╗
; ║ PROCEDURES THREAD ║
; ╚═══════════════════╝
Procedure   Pc_Initialisation_ThreadAnalyseDossiers()
  Global *DonneesThread.DONNEESTHREAD=AllocateStructure(DONNEESTHREAD)
EndProcedure
Procedure.i Fc_Thread_Demarrage(*ArgThread.INFOSTHREAD,*ArgProcedure) ; ThreadID
  With *ArgThread
    If Not IsThread(\IDThread)
      \Fin=#False
      \Pause=#False
      \IDThread=CreateThread(*ArgProcedure,*ArgThread)
    EndIf
    ProcedureReturn \IDThread
  EndWith
EndProcedure
Procedure   Pc_Thread_Arret(*ArgThread.INFOSTHREAD,ArgAttente=1000)
  With *ArgThread
    If IsThread(\IDThread)
      \Fin=#True
      If \Pause
        \Pause=#False
        SignalSemaphore(\Signal)
      EndIf
      If ArgAttente
        If WaitThread(\IDThread,ArgAttente)=0
          KillThread(\IDThread)
        EndIf
        \IDThread=0
        \Pause=#False
        \Fin=#False
        If \Signal
          FreeSemaphore(\Signal)
          \Signal=0
        EndIf
      EndIf
    EndIf
  EndWith
EndProcedure
Procedure.a Fc_Thread_Liberation(*ArgThread.INFOSTHREAD,ArgArret.a=#True,ArgAttente=1000) ; True or False
  With *ArgThread
    If IsThread(\IDThread)
      If ArgArret
        Pc_Thread_Arret(*ArgThread,ArgAttente)
        FreeStructure(*ArgThread)
        ProcedureReturn #True
      Else
        ProcedureReturn #False
      EndIf
    Else
      If \Signal
        FreeSemaphore(\Signal)
      EndIf
      FreeStructure(*ArgThread)
      ProcedureReturn #True
    EndIf
  EndWith
EndProcedure
Procedure   Pc_Thread_Pause(*ArgThread.INFOSTHREAD)
  With *ArgThread
    If IsThread(\IDThread)
      If Not \Signal
        \Signal=CreateSemaphore()
      EndIf
      If Not \Pause
        \Pause=#True
      EndIf
    EndIf
  EndWith
EndProcedure
Procedure   Pc_Thread_Reprise(*ArgThread.INFOSTHREAD)
  With *ArgThread
    If IsThread(\IDThread)
      If \Pause
        \Pause=#False
        SignalSemaphore(\Signal)
      EndIf
    EndIf
  EndWith
EndProcedure
;-══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
; ╔══════════════╗
; ║ FENETRES XML ║
; ╚══════════════╝
Procedure.a Fc_Fenetres_OuvertureDialogueXML(ArgArbreXML.u,ArgDialogueXML.u,ArgChaineXML.s,ArgNomFenetre.s,ArgIdFenetreParente.i=0)
  Protected.i NoDialogue
  Protected.a TypeErreur
  Protected.s TitreMSG="Application",MsgErreur

  If ArgChaineXML
    If ParseXML(ArgArbreXML,ArgChaineXML)
      If XMLStatus(ArgArbreXML)=#PB_XML_Success
        If CreateDialog(ArgDialogueXML)
          If ArgIdFenetreParente<>0
            NoDialogue=OpenXMLDialog(ArgDialogueXML,ArgArbreXML,ArgNomFenetre,0,0,0,0,ArgIdFenetreParente)
          Else
            NoDialogue=OpenXMLDialog(ArgDialogueXML,ArgArbreXML,ArgNomFenetre,0,0,0,0)
          EndIf
          If NoDialogue
            FreeXML(ArgArbreXML)
            ProcedureReturn #True
          Else
            TypeErreur=3
          EndIf
          FreeDialog(ArgDialogueXML)
        Else
          TypeErreur=2
        EndIf
        FreeXML(ArgArbreXML)
      Else
        TypeErreur=1
        MsgErreur="Erreur durant l'interprétation du dialogue XML !"
      EndIf
    Else
      TypeErreur=1
      MsgErreur="Erreur durant la lecture des données du dialogue XML !"
    EndIf
  EndIf
  Select TypeErreur
    Case 1:MsgErreur+"\n\n  No erreur : "+XMLStatus(ArgArbreXML)+" - "+XMLError(ArgArbreXML)+"\n  Ligne : "+Str(XMLErrorLine(ArgArbreXML))+" - Colonne : "+Str(XMLErrorPosition(ArgArbreXML))
    Case 2:MsgErreur="Erreur durant la création de la fenêtre de dialogue !"
    Case 3:MsgErreur="Erreur durant l'ouverture de la fenêtre de dialogue !\n\nErreur : "+DialogError(ArgDialogueXML)
  EndSelect
  MessageRequester(TitreMSG,UnescapeString(MsgErreur),#PB_MessageRequester_Error)
EndProcedure
; ╔═══════════════════════════════╗
; ║ PROCEDURES FENETRE PRINCIPALE ║
; ╚═══════════════════════════════╝
Procedure   Pc_Gestion_BtFPLancer(ArgEtat.a)
  Protected.s Chaine
  
  If GetGadgetData(#GAD_FP_BT_LANCER)<>ArgEtat
    Select ArgEtat
      Case 0:Chaine="&Lancer"
      Case 1:Chaine="&Pause"
      Case 2:Chaine="&Reprise"
    EndSelect
    SetGadgetText(#GAD_FP_BT_LANCER,Chaine)
    SetGadgetData(#GAD_FP_BT_LANCER,ArgEtat)
  EndIf
EndProcedure
Procedure   Pc_Gestion_BtFPQuitter(ArgEtat.a)
  Protected.s Chaine
  
  If GetGadgetData(#GAD_FP_BT_QUITTER)<>ArgEtat
    Select ArgEtat
      Case 0:Chaine="&Quitter"
      Case 1:Chaine="&Arrêter"
    EndSelect
    SetGadgetText(#GAD_FP_BT_QUITTER,Chaine)
    SetGadgetData(#GAD_FP_BT_QUITTER,ArgEtat)
  EndIf
EndProcedure
Procedure   Pc_Analyse_Dossier(*ArgDonneesThread.DONNEESTHREAD)
  Protected.FICHIER Fichier
  Protected.i NoDossier
  Protected.s Chemin,Extension
  
  ;Debug "Pc_Analyse_Dossier"
  With *ArgDonneesThread
    If \NiveauRecursif>0
      Chemin=\Chemin
    Else
      NextElement(\Dossiers())
      Chemin=\CheminOriginel
      Chemin=\Dossiers()
    EndIf
    \NiveauRecursif+1
  EndWith
  NoDossier=ExamineDirectory(#PB_Any,Chemin,"*.*")
  If NoDossier
    Debug "  Analyse : "+Chemin
    ;Debug "  NoDossier "+NoDossier
    While NextDirectoryEntry(NoDossier)
      If *ArgDonneesThread\Pause
        ;PostEvent(#MyEvent_ThreadGetFilesString,\Fenetre,0,0,;Fc_Affectation_Chaine(ChaineDonnees))
        WaitSemaphore(*ArgDonneesThread\Signal)
        ;PostEvent(#MyEvent_ThreadGetFilesString,\Fenetre,0,0,Fc_Affectation_Chaine(ChaineDonnees))
      EndIf
      If *ArgDonneesThread\Fin
        Break
      EndIf
      With Fichier
        \Nom=DirectoryEntryName(NoDossier)
        ;Debug "    Nom : "+\nom
        \Chemin=Chemin
        \Attributs=GetFileAttributes(\Chemin+\Nom)
        If DirectoryEntryType(NoDossier)=#PB_DirectoryEntry_Directory
          If \Nom<>"." And \Nom<>".."
            If Not (\Attributs&#PB_FileSystem_Hidden And \Attributs&#PB_FileSystem_System)
              Delay(10)
              *ArgDonneesThread\Chemin=\Chemin+\Nom+"\"
              Pc_Analyse_Dossier(*ArgDonneesThread)
            EndIf
          EndIf
        Else
          If GetGadgetText(#GAD_FP_LIB_NOMDOSSIER)<>\Chemin
            SetGadgetText(#GAD_FP_LIB_NOMDOSSIER,\Chemin)
          EndIf
          If Not (\Attributs&#PB_FileSystem_Hidden And \Attributs&#PB_FileSystem_System)
            SetGadgetText(#GAD_FP_LIB_NOMFICHIER,\Nom)
            \Taille=FileSize(\Chemin+\Nom)
            \DateModif=GetFileDate(\Chemin+\Nom,#PB_Date_Modified)
            ;Debug "      Fichier retenu : "+Fichier\Nom
            AddElement(Fichiers())
            Fichiers()=Fichier
          EndIf
        EndIf
      EndWith
      ClearStructure(@Fichier,FICHIER)
    Wend
    FinishDirectory(NoDossier)
  EndIf
  With *ArgDonneesThread
    \NiveauRecursif-1
    If \NiveauRecursif=0
      If \Fin
        PostEvent(#EVENMTTHREAD_ANALYSEANNULEE,\Fenetre,0,0,*ArgDonneesThread)
        \IDThread=0
      Else
        If ListIndex(*ArgDonneesThread\Dossiers())=ListSize(*ArgDonneesThread\Dossiers())-1
          PostEvent(#EVENMTTHREAD_ANALYSETERMINEE,\Fenetre,0,0,*ArgDonneesThread)
          \IDThread=0
        Else
          Pc_Analyse_Dossier(*ArgDonneesThread)
        EndIf
      EndIf
    EndIf
  EndWith
EndProcedure
; Procedures Boutons & Raccourcis
Procedure   Pc_Traitement_BtFPLancer()
  Protected.u NbDossiers,Compteur
  Protected.a EtatGadget=GetGadgetData(#GAD_FP_BT_LANCER)
  Protected.s Dossier
  
  Select EtatGadget
    Case 0 ; Lancer
      If Not IsThread(*DonneesThread\IDThread)
        NbDossiers=CountGadgetItems(#GAD_FP_LS_DOSSIERSSOURCE)
        If NbDossiers
          SetGadgetText(#GAD_FP_LIB_ACTION,"Inventaire des dossiers et fichiers à déplacer : ")
          ClearList(Fichiers())
          ClearList(*DonneesThread\Dossiers())
          NbDossiers-1
          For Compteur=0 To NbDossiers
            Dossier=GetGadgetItemText(#GAD_FP_LS_DOSSIERSSOURCE,Compteur)
            Select UCase(Dossier)
              Case "[BUREAU]":Dossier=GetUserDirectory(#PB_Directory_Desktop)
              Case "[DOCUMENTS]":Dossier=GetUserDirectory(#PB_Directory_Documents)
              Case "[TÉLÉCHARGEMENTS]":Dossier=GetUserDirectory(#PB_Directory_Downloads)
            EndSelect
            With *DonneesThread
              \NiveauRecursif=0
              \Fenetre=#FEN_PRINCIPALE
              AddElement(*DonneesThread\Dossiers())
              *DonneesThread\Dossiers()=Dossier
              ;\Filtre=LCase(ArgFiltre)
            EndWith
          Next
          Pc_Gestion_BtFPLancer(1):Pc_Gestion_BtFPQuitter(1)
          ResetList(*DonneesThread\Dossiers())
          Fc_Thread_Demarrage(*DonneesThread,@Pc_Analyse_Dossier())
          Debug *DonneesThread\IDThread
          If *DonneesThread\IDThread
            WaitThread(*DonneesThread\IDThread)
          EndIf
        EndIf
      Else
      EndIf
    Case 1 ; Pause
      If IsThread(*DonneesThread\IDThread) And Not *DonneesThread\Pause
        Pc_Thread_Pause(*DonneesThread)
        Pc_Gestion_BtFPLancer(2)
      EndIf
    Case 2 ; Reprendre
      If IsThread(*DonneesThread\IDThread) And *DonneesThread\Pause
        Pc_Thread_Reprise(*DonneesThread)
        Pc_Gestion_BtFPLancer(1)
      EndIf
  EndSelect
EndProcedure
Procedure   Pc_Traitement_BtFPQuitter()
  Protected.a EtatGadget=GetGadgetData(#GAD_FP_BT_Quitter)
  
  Select EtatGadget
    Case 0 ; Quitter
      Fc_Thread_Liberation(*DonneesThread)
      End
    Case 1 ; Arrêter
      If IsThread(*DonneesThread\IDThread)
        Pc_Thread_Pause(*DonneesThread)
        If MessageRequester("Analyse de dossiers","Confirmer l'arrêt de l'analyse des dossiers ?",#PB_MessageRequester_YesNo|#MB_ICONQUESTION)=#PB_MessageRequester_Yes
          Pc_Thread_Arret(*DonneesThread)
          ClearList(Fichiers())
        Else
          Pc_Thread_Reprise(*DonneesThread)
        EndIf
      EndIf
  EndSelect
EndProcedure
Procedure   Pc_Reinitialisation_GadgetsFP()
  SetGadgetText(#GAD_FP_LIB_ACTION,"")
  SetGadgetText(#GAD_FP_LIB_NOMDOSSIER,"")
  SetGadgetText(#GAD_FP_LIB_NOMFICHIER,"")
  Pc_Gestion_BtFPLancer(0)
  Pc_Gestion_BtFPQuitter(0)
EndProcedure  
;
Procedure   Pc_FenPrincipale_GestionEvenements()
  Define.l Evenmt,EvenmtMenu,TypeEvenmt,NoGadget

  Repeat
    Evenmt=WaitWindowEvent()
    TypeEvenmt=EventType()
    Select Evenmt
      Case #PB_Event_Gadget
        NoGadget=EventGadget()
        Select TypeEvenmt
          Case #PB_EventType_LeftClick
            Select NoGadget
              Case #GAD_FP_BT_LANCER:Pc_Traitement_BtFPLancer()
              Case #GAD_FP_BT_QUITTER:Pc_Traitement_BtFPQuitter()
            EndSelect
        EndSelect
      Case #PB_Event_CloseWindow
        End
      Case #EVENMTTHREAD_ANALYSETERMINEE
        ;Debug "Analyse dossiers terminée !"
        Pc_Gestion_BtFPLancer(0):Pc_Gestion_BtFPQuitter(0)
      Case #EVENMTTHREAD_ANALYSEANNULEE
        ;Debug "Annulation analyse dossiers !"
        Pc_Reinitialisation_GadgetsFP()
    EndSelect
  ForEver
EndProcedure
Procedure   Pc_FenPrincipale_Affichage()
  Protected.a ValeurRetour
  Protected.s ChaineXML
  
  ChaineXML="<?xml version='1.0' encoding='UTF-16'?>"+Chr(10)+ ;{
            "<dialogs>"+Chr(10)+
            "  <window id='"+Str(#FEN_PRINCIPALE)+"' name='FEN_PRINCIPALE' text='"+Mc_FormatageXML("Synchronisation de fichiers")+"' xpos='106' ypos='102' width='800' minwidth='auto' minheight='auto' margin='10' flags='#PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_SizeGadget'>"+Chr(10)+
            "    <vbox expand='item:1' spacing='8'>"+Chr(10)+
            "      <gridbox id='"+Str(#GAD_FP_BG_DOSSIERS)+"' name='GAD_FP_BG_DOSSIERS' columns='1' colspacing='4' colexpand='item:1' rowexpand='item:1'>"+Chr(10)+
            "        <listview id='"+Str(#GAD_FP_LS_DOSSIERSSOURCE)+"' name='GAD_FP_LS_DOSSIERSSOURCE' height='160' flags='#PB_ListView_Multiselect'/>"+Chr(10)+
            "        <text id='"+Str(#GAD_FP_LIB_DOSSIERCIBLE)+"' name='GAD_FP_LIB_DOSSIERCIBLE' text='"+Mc_FormatageXML(" ")+"'/>"+Chr(10)+
            "      </gridbox>"+Chr(10)+
            "      <singlebox expand='no' align='top,left' height='16' margin='0'/>"+Chr(10)+
            "      <text id='"+Str(#GAD_FP_LIB_ACTION)+"' name='GAD_FP_LIB_ACTION' text='"+Mc_FormatageXML(" ")+"'/>"+Chr(10)+
            "      <gridbox id='"+Str(#GAD_FP_BG_INFOS)+"' name='GAD_FP_BG_INFOS' invisible='yes' columns='3' colspacing='4' colexpand='item:3'>"+Chr(10)+
            "        <singlebox expand='no' width='0'/>"+Chr(10)+
            "        <text id='"+Str(#GAD_FP_LIB_DOSSIER)+"' name='GAD_FP_LIB_DOSSIER' text='"+Mc_FormatageXML("Dossier : ")+"'/>"+Chr(10)+
            "        <text id='"+Str(#GAD_FP_LIB_NOMDOSSIER)+"' name='GAD_FP_LIB_NOMDOSSIER' flags='#PB_Text_Border'/>"+Chr(10)+
            "        <singlebox expand='no' width='0'/>"+Chr(10)+
            "        <text id='"+Str(#GAD_FP_LIB_FICHIER)+"' name='GAD_FP_LIB_FICHIER' text='"+Mc_FormatageXML("Fichier : ")+"'/>"+Chr(10)+
            "        <text id='"+Str(#GAD_FP_LIB_NOMFICHIER)+"' name='GAD_FP_LIB_NOMFICHIER' flags='#PB_Text_Border'/>"+Chr(10)+
            "      </gridbox>"+Chr(10)+
            "      <singlebox expand='no' align='top,left' width='0' height='8' margin='0'/>"+Chr(10)+
            "      <hbox expand='item:1' spacing='8'>"+Chr(10)+
            "        <singlebox/>"+Chr(10)+
            "        <button id='"+Str(#GAD_FP_BT_LANCER)+"' name='GAD_FP_BT_LANCER' text='"+Mc_FormatageXML("&Lancer")+"' width='120'/>"+Chr(10)+
            "        <button id='"+Str(#GAD_FP_BT_QUITTER)+"' name='GAD_FP_BT_QUITTER' text='"+Mc_FormatageXML("&Quitter")+"' width='120'/> "+Chr(10)+
            "      </hbox>"+Chr(10)+
            "      <singlebox width='22'/>"+Chr(10)+
            "    </vbox>"+Chr(10)+
            "  </window>"+Chr(10)+
            "</dialogs>" ;}
  ValeurRetour=Fc_Fenetres_OuvertureDialogueXML(#ARBRE_XML,#XML_FENPRINCIPALE,ChaineXML,"FEN_PRINCIPALE")
  If ValeurRetour
    ; Ajout Dossiers sources par défaut
    AddGadgetItem(#GAD_FP_LS_DOSSIERSSOURCE,-1,"[Bureau]")
    AddGadgetItem(#GAD_FP_LS_DOSSIERSSOURCE,-1,"[Documents]")
    AddGadgetItem(#GAD_FP_LS_DOSSIERSSOURCE,-1,"[Téléchargements]")
    SetGadgetItemData(#GAD_FP_LS_DOSSIERSSOURCE,0,0)
    SetGadgetItemData(#GAD_FP_LS_DOSSIERSSOURCE,1,0)
    SetGadgetItemData(#GAD_FP_LS_DOSSIERSSOURCE,2,0)
    ;
    SetGadgetText(#GAD_FP_LIB_DOSSIERCIBLE,GetUserDirectory(#PB_Directory_ProgramData))
    SetGadgetText(#GAD_FP_LIB_DOSSIERCIBLE,GetUserDirectory(#PB_Directory_Documents))
    Pc_Initialisation_ThreadAnalyseDossiers()
    Pc_FenPrincipale_GestionEvenements()
  EndIf
EndProcedure
;-══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
; ╔══════════════════╗
; ║ MODULE PRINCIPAL ║
; ╚══════════════════╝
Pc_FenPrincipale_Affichage()
End

; IDE Options = PureBasic 6.02 LTS (Windows - x64)
; CursorPosition = 467
; FirstLine = 434
; EnableXP
; DPIAware
; CompileSourceDirectory
; EnablePurifier
Merci d'avance pour vos lumières.

Note : En dehors de la problématique de la gestion du thread, ne tenez pas compte de bizarreries que vous pourriez constater sur le reste du code car, je le rappelle, j'ai tenté de l'expurger au mieux de bouts inutiles pour la démonstration...
Dernière modification par boddhi le mar. 22/août/2023 23:51, modifié 1 fois.
Avatar de l’utilisateur
cage
Messages : 604
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Utilisation de WaitThread

Message par cage »

Bonjour boddhi,

Pourquoi ne pas créer un Thread principal dans lequel tu ferais tourner tes Threads

Par contre, je ne sais pas (n'ayant pas essayé) si WaitThread() fonctionne dans un Thread

Je lance régulièrement des Threads a partir d'un Thread entrain de tourner, et ça fonctionne comme attendu.

J'ai aussi des Threads permanents qui en fonction d’événements extérieurs au Thread permanent font ce qu'ils doivent.

Code : Tout sélectionner

Procedure main_thread(*v)
	Shared others_variables, others_variables$
	
	Protected somes_variables, somes_variables$
	
	Repeat
		; tout le traitement se fait ici
		Delay(5000)
	ForEver
	; on ne passe jamais ici
EndProcedure

thread = CreateThread(@main_thread(), #True)
cage
■ Win10 Pro 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.12 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Utilisation de WaitThread

Message par boddhi »

Salut cage,

Merci pour ta réponse.

Je ne sais pas si c'est la bonne solution.

Au risque de me répéter, je suis vraiment mais vraiment une quiche en matière de threads. Je suis de la vieille école du séquentiel à une époque où les processeurs étaient si lents que t'avais le temps d'aller te faire cuire un steack entre deux opérations.
J'exagère bien évidemment mais j'ai connu l'époque où lorsque tu lançais une boucle, rien ne t'empêchait d'intercepter un évènement de l'UI dans des délais raisonnables sans avoir forcément besoin de faire appel à des threads...

Pour en revenir à ta proposition, je ne sais pas... Selon moi (et peut-être que je me trompe pcq je n'appréhende pas les threads de la bonne manière) , la boucle principale doit demeurer la boucle d'interception des évènements car le thread que j'ai conçu n'est qu'une étape dans un processus plus large où seront nécessaires des interactions avec l'utilisateur, la création de nouvelles fenêtres (donc avec encore des évènements à intercepter), etc.

Selon ma logique :
• J'ouvre une UI avec une boucle permanente d'interception d'évènements
• L'utilisateur lance une action
• Si besoin, cette action est threadée si plusieurs traitements "longs" doivent s'enchaîner et qu'un évènement déclenché par l'utilisateur, tel que mise en pause ou annulation de l'action/traitement en cours, puisse être autorisé et intercepté.

d'où ma conception intellectuelle et programmatique :
1) Déclenchement d'une action principale par l'utilisateur
2) Lancement d'un thread (CreateThread()) pour un traitement donné
3) Mise en pause (WaitThread()) de l'action principale jusqu'à la fin du thread avec possibilité pour l'utilisateur d'arrêter complètement l'action en ne poursuivant pas les traitements suivants prévus et ainsi sauter directement à l'étape 7)
4) Attente de la fin normale du thread
5) Retour à l'action principale
6) on recommence les opérations 2) à 6) jusqu'à ce que tous les traitements soient effectués
7) Retour à (et fin de) l'action principale
8 ) Possibilité pour l'utilisateur de recommencer depuis 1)

Du séquentiel threadé, en somme.

Comme tu l'auras compris, mon souci se situe au niveau de l'étape 3. Dans mon code, le WaitThread() bloque et l'action principale et le thread en lui-même.

Mon approche conceptuelle ne me paraît pourtant pas mauvaise mais, encore une fois, peut-être trompé-je et que le WaitThread n'est pas utilisé, s'il doit l'être, au bon endroit ou de la bonne manière... ou tout simplement que cette approche n'est pas la bonne.
Avatar de l’utilisateur
cage
Messages : 604
Inscription : ven. 16/oct./2015 18:22
Localisation : France
Contact :

Re: Utilisation de WaitThread

Message par cage »

Voici un exemple avec une boucle principale et un thread qui tourne en permanence.
Si on clique sur le menu Action/Start process, un premier thread est lancé et attend avec WaitThread() qu'il se termine pour lancer un second thread.
On peut imaginer plusieurs thread qui s'activerait les uns après les autres avec plusieurs WaitThread() pour assurer que l'un soit terminé avant de lancer le suivant.
Dans l'exemple, la variable ACTION sert a lancer ou arrêter l'action a réaliser via le menu Action/Start process qui change en Action/Stop process

Code : Tout sélectionner

EnableExplicit

CompilerIf Not #PB_Compiler_Thread
  CompilerError "L'option 'Activer la gestion des thread' doit etre activée."
CompilerEndIf

OnErrorGoto(?ErrorHandler)

SetCurrentDirectory(GetPathPart(ProgramFilename()))

CompilerIf #PB_Compiler_Debugger
  ;;; Nous sommes dans l'IDE
  #WinApp = 999
CompilerElse
  #WinApp = 56
CompilerEndIf

Enumeration Menus
  #MenuApp
EndEnumeration

Enumeration FormMenu
  #MenuAppQuit
  #MenuAppRun
EndEnumeration

Enumeration Gadgets
  #PBar1
  #PBar2
  #PBar3
EndEnumeration

#pstop$  = "Stop process"
#pstart$ = "Start process"

Declare main_thread(*v)
Declare pbar1_thread(*v)
Declare pbar2_thread(*v)
Declare pbar3_thread(*v)

Global thread0, thread1, thread2, thread3

Global ACTION=#False

Procedure main_thread(*v)
  Protected N, thread1, thread2, thread3
  
  Repeat
    ; tout le traitement se fait ici
    If Not IsThread(thread1)
      thread1=CreateThread(@pbar1_thread(), #True)
    EndIf
    If ACTION
      thread2=CreateThread(@pbar2_thread(), #True)
      WaitThread(thread2)
    EndIf
    If ACTION
      thread3=CreateThread(@pbar3_thread(), #True)
      WaitThread(thread3)
      ACTION=#False
      SetMenuItemText(#MenuApp, #MenuAppRun, #pstart$)
    EndIf
    Delay(1000)
  ForEver
  ; on ne passe jamais ici
EndProcedure

Procedure pbar1_thread(*v)
  Protected N
  Repeat
    ; tout le traitement se fait ici
    For N=1 To 100
      SetGadgetState(#PBar1, N)
      Delay(50)
    Next
  ForEver
EndProcedure

Procedure pbar2_thread(*v)
  Protected N
  ; tout le traitement se fait ici
  For N=1 To 100
    SetGadgetState(#PBar2, N)
    Delay(50)
  Next
EndProcedure

Procedure pbar3_thread(*v)
  Protected N
  ; tout le traitement se fait ici
  For N=1 To 100
    SetGadgetState(#PBar3, N)
    Delay(50)
  Next
EndProcedure

#WinAppWidth  = 300
#WinAppHeight = 200

Global X,Y,W,H

ExamineDesktops()
X = (DesktopWidth(0) -#WinAppWidth)/2
Y = (DesktopHeight(0)-#WinAppHeight)/2

Define FLAGS = #PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_ScreenCentered

If OpenWindow(#WinApp, X, Y, #WinAppWidth, #WinAppHeight, "Utilisation des Threads", FLAGS)

  If CreateImageMenu(#MenuApp, WindowID(#WinApp), #PB_Menu_ModernLook)
    MenuTitle("Fichier")
    MenuItem(#MenuAppQuit, "Quitter"+Chr(9)+"Ctrl+Q")
    MenuTitle("Action")
    MenuItem(#MenuAppRun, #pstart$)
  EndIf
  
  X=3:Y=3:W=#WinAppWidth-(X*2):H=20
  ProgressBarGadget(#PBar1, X, Y, W, H, 1, 100, #PB_ProgressBar_Smooth)
  Y=H+(Y*2)
  ProgressBarGadget(#PBar2, X, Y, W, H, 1, 100, #PB_ProgressBar_Smooth)
  Y=(H*2)+(3*3)
  ProgressBarGadget(#PBar3, X, Y, W, H, 1, 100, #PB_ProgressBar_Smooth)
  
  AddKeyboardShortcut(#WinApp, #PB_Shortcut_Control|#PB_Shortcut_Q, #MenuAppQuit) ; quit
  AddKeyboardShortcut(#WinApp, #PB_Shortcut_Control|#PB_Shortcut_X, #MenuAppQuit) ; quit
  
  thread0 = CreateThread(@main_thread(), #True)
  
  Define Event, indexMenu
  
  Repeat
    Event = WaitWindowEvent(20)
    Select Event
      Case #PB_Event_Menu
        indexMenu = EventMenu()
        Select indexMenu
          Case #MenuAppRun
            Select GetMenuItemText(#MenuApp, #MenuAppRun)
              Case #pstart$
                SetMenuItemText(#MenuApp, #MenuAppRun, #pstop$)
                ACTION=#True
              Case #pstop$
               ;SetMenuItemText(#MenuApp, #MenuAppRun, #pstart$)
                ACTION=#False
            EndSelect
          Case #MenuAppQuit : Break
        EndSelect
      Case #PB_Event_CloseWindow
        Break
    EndSelect
  ForEver
EndIf

ErrorHandler:

If IsThread(thread0) : KillThread(thread0) : EndIf
If IsThread(thread1) : KillThread(thread1) : EndIf
If IsThread(thread2) : KillThread(thread2) : EndIf
If IsThread(thread3) : KillThread(thread3) : EndIf

End
Ce n'est qu'une base a laquelle il faudrait ajouter quelques contrôles.
cage
■ Win10 Pro 64-bit (Intel Celeron CPU N2920 @ 1.86GHz, 4,0GB RAM, Intel HD Graphics) & PB 6.12 LTS
■ Vivre et laisser vivre.
■ PureBasic pour le fun
■ Gérard sur le forum Anglais
■ Mes sites: http://pbcage.free.fr - http://yh.toolbox.free.fr
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Utilisation de WaitThread

Message par boddhi »

Merci cage pour ton code de démo. Je vais étudier cela.

Pour l'instant, je suis un peu sur plusieurs projets à la fois : Appels à l'API MusicBrainz, lecture des données EXIF des fichiers HEIC pour compléter ma lib EXIFRead et enfin celui posté plus haut.

Et pour ce dernier, je me concentre en ce moment sur la programmation de l'ensemble des traitements/modules et de leur articulation entre eux que devra permettre l'appli. Dans l'attente de régler définitivement, si possible, le pb du WaitThread, j'ai commencé à contourner cet écueil en obligeant l'utilisateur à lancer les étapes les unes après les autres et donc thread après thread. Tout cela va me prendre un certain temps mais, une fois fini, je verrai si ET comment je peux intégrer ton exemple à mon source. J'ai également un code d'Ollivier concernant aussi les threads à éplucher, étudier et comprendre. 😰 :mrgreen:

Je relancerai très vraisemblablement le topic quand j'en serai là :wink:

Merci encore pour ton temps et ton code.
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: Utilisation de WaitThread

Message par boddhi »

Pour info,

Je suis enfin parvenu à mes fins après beaucoup de tâtonnements 😰 :D

Sans trop entrer dans les détails, j'ai lâché l'affaire avec WaitThread().

Bien qu'ayant plusieurs procédures threadées mais seulement besoin d'un seul thread qui tourne à la fois, je suis passé par l'utilisation d'un mutex :
- Je crée un mutex unique en début de programme.
- Je lance l'une de mes procédures threadées via CreateThread
- A l'intérieur de la procédure, je 'lock' le mutex
- A la fin de la procédure, J'unlocke le mutex et je le kill
- Je kill le thread
Et à l'intérieur de chaque procédure, je teste si une demande de pause, reprise ou arrêt a été effectuée par l'utilisateur à l'aide d'une variable structurée et demande à laquelle je satisfais via PauseThread et ResumeThread.

Merci à tous ceux qui m'auront répondu, aiguillé et aidé sur ce topic ou sur d'autres et permis d'avancer un peu (car y a encore du boulot !!!) sur la compréhension et la maîtrise des threads.
Avatar de l’utilisateur
microdevweb
Messages : 1802
Inscription : mer. 29/juin/2011 14:11
Localisation : Belgique

Re: [Clos/Résolu] Utilisation de WaitThread

Message par microdevweb »

Bonjour,

Voici le lien d'un tuto que j'avais fais sur le forum pour utiliser les threads. Dans le cadre de mon boulot, même si je n'utilise pas Pb mais C ou C# j'utilise très frequement le Produceur consumer.
Tuto multi threading

Cordialement.
Windows 10 64 bits PB: 5.70 ; 5.72 LST
Work at Centre Spatial de Liège
boddhi
Messages : 604
Inscription : lun. 26/avr./2010 16:14
Localisation : S 48° 52' 31'' / O 123° 23' 33''

Re: [Clos/Résolu] Utilisation de WaitThread

Message par boddhi »

Salut Microdevweb
microdevweb a écrit : Voici le lien d'un tuto que j'avais fais sur le forum pour utiliser les threads.
C'est quoi cette idée de réveiller un mort (mon post) ? :mrgreen: :lol: :wink:

Plus sérieusement, bien évidemment, j'avais vu ton tuto sur le sujet mais pour être très honnête, le sujet était tellement nébuleux pour moi à l'époque que je n'y avais pas pigé grand chose. Non pas que la qualité de tes explications soit à remettre en cause mais mon esprit à vrai dire un peu rétif d'emblée et des interrogations qui ne trouvaient pas de réponse ne m'avaient pas permis de bien comprendre le principe. Ce qui est encore un peu vrai à tel point que je n'utilise les threads que dans leur plus simple méthode et un seul à la fois.

Je suis un (vieil) autodidacte et me suis formé à l'école du séquentiel. J'ai eu une longue pause, faute de temps disponible, dans la programmation et j'ai hélas loupé cette évolution programmatique quand elle arrivée jusqu'à devenir quasi incontournable et j'ai continué à me satisfaire de mes acquis avec son lot d'inconvénients.

En tout cas, merci. :wink:
Avatar de l’utilisateur
Guillot
Messages : 672
Inscription : jeu. 25/juin/2015 16:18

Re: [Clos/Résolu] Utilisation de WaitThread

Message par Guillot »

microdevweb a écrit : jeu. 11/avr./2024 14:21 Bonjour,

Voici le lien d'un tuto que j'avais fais sur le forum pour utiliser les threads. Dans le cadre de mon boulot, même si je n'utilise pas Pb mais C ou C# j'utilise très frequement le Produceur consumer.
Tuto multi threading

Cordialement.
heureux de trouver le big boss des threads (en plus sur le forum français)
je voudrais savoir si tu connais un moyen de lancer un thread uniquement si y'a un coeur dispo

pour mes décors 3D, je modifie le niveau de détails des mesh dans un thread
pas de problème si son exécution est différée, en revanche ce qui me gène c'est de geler le thread principal
jusqu'ici je lance un thread que si le nombre de thread en cour d'execution (lancer depuis PB) est inferieur au nombre de coeur -1
mais ça gele parfois la machine quelques instant, c'est ennuyeux pour certains jeux d'action (style course de voiture)
Répondre