Page 1 sur 1

Une approche des threads

Publié : jeu. 19/mai/2011 14:11
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.

Re: Une approche des threads

Publié : jeu. 19/mai/2011 16:37
par nico
très bon exemple pour les débutants :)

Re: Une approche des threads

Publié : ven. 20/mai/2011 1:05
par Ar-S
il faut aussi penser à KillThread() non ?

Re: Une approche des threads

Publié : mar. 24/mai/2011 13:37
par pat
KillThread pas du tout recommandé (mais possible) par la doc de PB (problème de pile).
Donc il vaut mieux faire autrement.

Re: Une approche des threads

Publié : mar. 24/mai/2011 13:53
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 ^^

Re: Une approche des threads

Publié : mar. 24/mai/2011 14:52
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

Re: Une approche des threads

Publié : mar. 24/mai/2011 15:00
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é.

Re: Une approche des threads

Publié : mar. 24/mai/2011 15:30
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: