Une approche des threads

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Avatar de l’utilisateur
falsam
Messages : 7317
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Une approche des threads

Message par falsam »

Je commence à m’intéresser à la gestion des threads et je vais essayer d'expliquer la façon dont je l'ai apréhendé.

Les threads permettent à un programme d'effectuer plusieurs opérations en même temps (Presque en même temps). Cela devient utile lorsque celui-ci doit effectuer une tâche relativement longue et que vous voulez tout de même permettre à l'utilisateur d'agir sur le programme.

A titre d'exemple
Je code une fenetre (#MainForm)
avec un bouton (#StartStop ) qui a un rôle d’interrupteur
qui lance une procédure(Count()) contenant une boucle. Durant cette boucle on teste l'état On/Off du bouton pour stopper ou pas la procédure.

Sans gestion de thread, si je clique sur le bouton qui lance la procédure, je suis obligé d'attendre la fin de la boucle car je ne peux plus intervenir sur le bouton poru stopper cette procédure.

Code : Tout sélectionner

Enumeration
  #MainForm
  #StartStop
  #Result
EndEnumeration


Procedure Count()
  For i=0 To 10000
    SetGadgetText(#Result, "Compteur = "+Str(i))
    If GetGadgetData(#StartStop)=#False
      Break
    EndIf
  Next i
EndProcedure


Procedure MainFormShow()
  OpenWindow(#MainForm,0,0,300,200,"Exemple sans Thread",#PB_Window_ScreenCentered | #PB_Window_SystemMenu)
  TextGadget(#Result, 20 ,30, 120, 20, "Compteur = 0")
  ButtonGadget(#StartStop, 200, 25, 70, 20, "Start/Stop") 
EndProcedure

MainFormShow()

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #StartStop
          If GetGadgetData(#StartStop)=#False
            SetGadgetData(#StartStop, #True) ;Indiquer au bouton qu'il est actif
            Count()
          Else
            SetGadgetData(#StartStop, #False) ;Indique au bouton qu'il est plus actif
          EndIf
          
      EndSelect
    Case #PB_Event_CloseWindow
      End
  EndSelect
ForEver
C'est la qu'intervient la notion de thread

Je vais lancer la procédure Count en parallèle à l’exécution du programme principale.

je remplace Count() par CreateThread(@Count(), 0)

Code : Tout sélectionner

Enumeration
  #MainForm
  #StartStop
  #Result
EndEnumeration


Procedure Count(*Value)
  For i=0 To 10000
    SetGadgetText(#Result, "Compteur = "+Str(i))
    If GetGadgetData(#StartStop)=#False
      Break
    EndIf
  Next i
EndProcedure


Procedure MainFormShow()
  OpenWindow(#MainForm,0,0,300,200,"Exemple avec Thread",#PB_Window_ScreenCentered | #PB_Window_SystemMenu)
  TextGadget(#Result, 20 ,30, 120, 20, "Compteur = 0")
  ButtonGadget(#StartStop, 200, 25, 70, 20, "Start/Stop") 
EndProcedure

MainFormShow()

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #StartStop
          If GetGadgetData(#StartStop)=#False
            SetGadgetData(#StartStop, #True) ;Indiquer au bouton qu'il est actif
            CreateThread(@Count(), 0)
          Else
            SetGadgetData(#StartStop, #False)
          EndIf
          
      EndSelect
    Case #PB_Event_CloseWindow
      End
  EndSelect
ForEver
Maintenant je peux lancer ma procédure et la stopper quand je le souhaite.
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
nico
Messages : 3702
Inscription : ven. 13/févr./2004 0:57

Re: Une approche des threads

Message par nico »

très bon exemple pour les débutants :)
Avatar de l’utilisateur
Ar-S
Messages : 9539
Inscription : dim. 09/oct./2005 16:51
Contact :

Re: Une approche des threads

Message par Ar-S »

il faut aussi penser à KillThread() non ?
~~~~Règles du forum ~~~~
⋅.˳˳.⋅ॱ˙˙ॱ⋅.˳Ar-S ˳.⋅ॱ˙˙ॱ⋅.˳˳.⋅
W11x64 PB 6.x
Section HORS SUJET : ICI
LDV MULTIMEDIA : Dépannage informatique & mes Logiciels PB
UPLOAD D'IMAGES : Uploader des images de vos logiciels
pat
Messages : 411
Inscription : mar. 24/nov./2009 15:13

Re: Une approche des threads

Message par pat »

KillThread pas du tout recommandé (mais possible) par la doc de PB (problème de pile).
Donc il vaut mieux faire autrement.
Avatar de l’utilisateur
Ar-S
Messages : 9539
Inscription : dim. 09/oct./2005 16:51
Contact :

Re: Une approche des threads

Message par Ar-S »

Ok. J'ai toujours énormément de mal avec les threads (et les timers aussi d'ailleurs), je n'arrive pas à assimiler leur fonctionnement dans PB.
Et pourtant j'en ai fait des essais (et lu des tutos)..

En tout cas ce petit exemple est le bienvenu. Si d'autres pouvaient suivre.
Genre Afficher le nom des fichiers traités lors d'une opération de resizeimage par glisser/déposer
Je bosse sur un nouveau redimenssionneurs d'images (je bloque resize 3 pour tiny photo resizer...) et j'hésite à réutiliser les threads/timers pour ça tellement j'en chie avec.

Image preview ^^
~~~~Règles du forum ~~~~
⋅.˳˳.⋅ॱ˙˙ॱ⋅.˳Ar-S ˳.⋅ॱ˙˙ॱ⋅.˳˳.⋅
W11x64 PB 6.x
Section HORS SUJET : ICI
LDV MULTIMEDIA : Dépannage informatique & mes Logiciels PB
UPLOAD D'IMAGES : Uploader des images de vos logiciels
Avatar de l’utilisateur
falsam
Messages : 7317
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: Une approche des threads

Message par falsam »

Comment parcourir un dossier avec une fonction récursive, commencer à afficher le résultat dans une liste, visualiser cette liste pendant l'éxécution de la fonction récursive et enfin stopper le scan du dossier quand on veut. C'est à travers cet autre exemple illustrant l'utilisation des threads que vous découvrirez cela.

Code : Tout sélectionner

Enumeration
  #MainForm
  #StartStop
  #ListFolder
  #CountFiles
EndEnumeration

Global CountFiles.i

;Parcourir un dossier (Fonction recurssive by Flype il me semble)
Procedure.s ParseDirectory(folder.s, id.l = 0)
  If Right(folder, 1) <> "\"
    folder + "\"
  EndIf
  
  If ExamineDirectory(id, folder, "*.*")
    While NextDirectoryEntry(id) And GetGadgetData(#StartStop)=#True
      If DirectoryEntryName(id) <> "." And DirectoryEntryName(id) <> ".."
        AddGadgetItem(#ListFolder,-1, folder +Chr(10)+DirectoryEntryName(id))
        countFiles+1
        SetGadgetText(#CountFiles, Str(CountFiles))
        If DirectoryEntryType(id) = #PB_DirectoryEntry_Directory
          ParseDirectory(folder + DirectoryEntryName(id), id + 1)
        EndIf
      EndIf
    Wend
    FinishDirectory(id)
  EndIf
EndProcedure 

Procedure StartThread(*Value)
  ClearGadgetItems(#ListFolder)
  CountFiles=0
  ParseDirectory(GetEnvironmentVariable("USERPROFILE"))
  While GetGadgetData(#StartStop)=#True
  Wend
EndProcedure

Procedure MainFormShow()
  OpenWindow(#MainForm,0,0,400,320,"Lecture d'un dossier(Thread)",#PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget)
  TextGadget(#PB_Any, 5, 10, 50, 20, "Fichier(s)")
  TextGadget(#CountFiles, 100, 10, 80, 20, "0")
  ListIconGadget(#ListFolder, 5, 30, 390,250, "Dossier", 200)
  AddGadgetColumn(#ListFolder, 1, "Fichier",200)
  ButtonGadget(#StartStop, 170, 290, 70, 20, "Start/Stop") 
EndProcedure

MainFormShow()

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #StartStop
          If GetGadgetData(#StartStop)=#False
            SetGadgetData(#StartStop, #True) ;Indiquer au bouton qu'il est actif
            CreateThread(@StartThread(), 0)
          Else
            SetGadgetData(#StartStop, #False)
          EndIf
          
      EndSelect
      
    Case #PB_Event_SizeWindow
      WWidth=WindowWidth(#MainForm)
      Wheight=WindowHeight(#MainForm)
      ResizeGadget(#ListFolder, #PB_Ignore, #PB_Ignore , WWidth-10, Wheight-75)
      ResizeGadget(#StartStop, (WWidth/2)-35, Wheight-30 , #PB_Ignore, #PB_Ignore)
      
    Case #PB_Event_CloseWindow
      End
  EndSelect
ForEver
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
Ar-S
Messages : 9539
Inscription : dim. 09/oct./2005 16:51
Contact :

Re: Une approche des threads

Message par Ar-S »

Merci beaucoup pour ce nouvel exemple!
Utiliser GetGadgetData(#gadget) comme déclencheur c'est tout bête mais je n'y avais jamais pensé.
~~~~Règles du forum ~~~~
⋅.˳˳.⋅ॱ˙˙ॱ⋅.˳Ar-S ˳.⋅ॱ˙˙ॱ⋅.˳˳.⋅
W11x64 PB 6.x
Section HORS SUJET : ICI
LDV MULTIMEDIA : Dépannage informatique & mes Logiciels PB
UPLOAD D'IMAGES : Uploader des images de vos logiciels
Avatar de l’utilisateur
flaith
Messages : 1487
Inscription : jeu. 07/avr./2005 1:06
Localisation : Rennes
Contact :

Re: Une approche des threads

Message par flaith »

Pas mal du tout
je rajouterais entre le while - wend un Delay(5) afin de diminuer le temps process pris par l'executable :wink:
Répondre