IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Lazarus Pascal Discussion :

[2.0.12] Besoin d'éclaircissement sur les threads


Sujet :

Lazarus Pascal

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 089
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 089
    Par défaut [2.0.12] Besoin d'éclaircissement sur les threads
    Bonjour,

    dans l'exemple Lazarus/version/examples/multithreading/waitforexample1 je trouve la procédure suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    procedure TThreadA.Execute;
    begin
      Form1.ThreadB := TThreadB.Create(false);
      // create event
      WaitForB := RTLEventCreate;
      while not Application.Terminated do begin
        Log('A: wait for B ...'); 
        // wait infinitely (until B wakes A)
        RtlEventWaitFor(WaitForB);
        Log('A: ThreadB.Counter=' + IntToStr(Form1.ThreadB.Counter));
      end;
    end;
    Regardez bien la 1re ligne, je la remets toute seule :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Form1.ThreadB := TThreadB.Create(false);
    et si je pose la question, c'est parce que nulle part dans le code je n'ai trouvé de destruction de ce ThreadB.
    Est-ce à dire que si je fais tourner le programme un certain temps, ça va consommer toute la mémoire disponible jusqu'au crash de la machine ?

    Quant à la ligne suivante, WaitForB := RTLEventCreate;, j'ai jeté un coup d'œil à l'aide et ça donne ça :
    Citation Envoyé par aide Lazarus
    RTLEventCreate creates and initializes a new RTL event. RTL events are used to notify other threads that a certain condition is met, and to notify other threads of condition changes (conditional variables).

    The function returns an initialized RTL event, which must be disposed of with RTLEventdestroy.
    et nulle part dans le code je ne trouve d'appel à RTLEventdestroy.
    Qu'est-ce que les spécialistes des threads en pensent ?

    Et enfin juste pour rire, quand on fait des manips tordues avec les threads, des fois l'exécutable ne répond plus et il faut le tuer, le pauvre, en cliquant sur la croix, mais il se défend par un dernier message :
    Nom : erreur1.png
Affichages : 377
Taille : 17,2 Ko

    Le gag c'est ce qui arrive après la validation :
    Nom : erreur2.png
Affichages : 361
Taille : 14,0 Ko

    Et cette information est confirmée par l'utilisation de l'outil locate (sous Linux, que du bonheur), qui ne trouve rien :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ locate futex-internal.h
    $ (vide)
    Au plaisir de vous lire...

  2. #2
    Expert confirmé
    Avatar de jurassic pork
    Homme Profil pro
    Bidouilleur
    Inscrit en
    Décembre 2008
    Messages
    4 192
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Bidouilleur
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2008
    Messages : 4 192
    Par défaut
    Hello,
    ce serait certainement plus propre en terminant les Threads quand on ferme la fenêtre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      DoneCriticalsection(ACriticalSection);
      if ThreadA<>nil then
        ThreadA.Terminate;
      if ThreadB<>nil then
        ThreadB.Terminate;
    end;
    Ami calmant, J.P (sans T)

  3. #3
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 089
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 089
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Ce qui est surprenant est que ThreadB n'appartienne pas à ThreadA mais à Form1
    Je vais tester.

    Citation Envoyé par Andnotor Voir le message
    Pas de FreeOnTerminate dans TThreadB ?
    Bah nan...

    Citation Envoyé par Andnotor Voir le message
    Sinon ça va dépendre de l'utilisation de TThreadA. Si cette tâche n'est créée qu'une fois au démarrage, il n'y aura aussi qu'un ThreadB.
    OK, vu : je me suis pris les pieds dans le tapis : il y a une procédure Execute, appelée une fois, qui crée le ThreadB et ensuite tourne en boucle avec un while pour incrémenter le compteur.
    J'y vois plus clair, merci.

    Citation Envoyé par Andnotor Voir le message
    Mais l'ensemble ne semble pas très cohérent.
    Citation Envoyé par jurassic pork Voir le message
    ce serait certainement plus propre en terminant les Threads quand on ferme la fenêtre :
    C'est pas moi, je le jure ! Ça vient du zip de Lazarus, 2.0.12 sur le host et 2.2.2 dans une MV mais c'est la même chose...

    Pendant que je vous tiens, j'ai une question liée à ce prog où il n'y a rien pour mettre le fourbi en pause, alors j'ai inventé ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var
      Form1: TForm1; 
      PleaseStop: boolean;
     
    procedure TForm1.btnStopClick(Sender: TObject);
    begin
      PleaseStop := True;
      Memo1.SetFocus; // juste pour vérifier que le prog n'est pas planté
    end;
     
    // pour le threadA et le threadB, dans leurs procedure ThreadX.Execute; respectives :
      while not Application.Terminated do begin
        if PleaseStop then Break; // ajout
    C'est correct, d'après vous ?

    Allez, je vais voir cette histoire d'owner du threadB : threadA ou fiche ? et je reviens.
    EDIT : coucou me revoilou, et ça aura été rapide :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    procedure TThreadA.Execute;
    begin
      // 2 tests Andnotor :
    //  Form1.ThreadA.ThreadB := TThreadB.Create(false);  // Error: identifier idents no member "ThreadB"
    //  ThreadA.ThreadB := TThreadB.Create(false);  // Error: identifier idents no member "ThreadB"
      // donc retour au fonctionnel :
      Form1.ThreadB := TThreadB.Create(false);

  4. #4
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 912
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 912
    Par défaut
    ThreadB doit être déclaré au niveau de TThreadA puisque sa durée de vie en est dépendante. Donc tu déplaces sa déclaration de TForm1 à TThreadA. Et même directement dans sa procédure Execute.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    procedure TThreadA.Execute;
    var
      ThreadB :TThreadB;
    begin
      ThreadB := TThreadB.Create;
     
      try
        while not Terminated do ...
      finally
        ThreadB.Free;
      end;
    end;
    Souviens-toi que tu ne maitrises pas la planification des threads, c'est l'OS qui s'en charge et dès lors tu ne peux garantir que Form1 (dans la tâche principale) soit détruite après la terminaison de ThreadA (à moins de mettre en place une synchro). Form1.ThreadB provoquera une VA si Form1 est détruite avant.

    C'est également Terminated du Thread sur lequel il faut boucler, pas celui de TApplication. Terminated est mis à vrai dès que l'objet TThread est libéré, que ce soit par un Free ou à la fermeture de l'application (libération implicite).

    PleaseStop n'a aucune utilité.
    Le thread au sens OS sera effectivement libéré puisque tu quittes la boucle mais pas l'objet TThread. ThreadA et ThreadB subsisteront inutilement jusqu'à la fin du programme.

    Si ThreadB est bien déclaré au niveau de TThreadA et que la boucle est conditionnée par Terminated du thread, il suffit d'un Free (FreeAndNil) pour terminer/libérer l'ensemble.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    procedure TForm1.btnStopClick(Sender: TObject);
    begin
      FreeAndNil(ThreadA);
    end;
    Tu le recrées simplement si tu en as à nouveau besoin :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure TForm1.btnStartClick(Sender: TObject);
    begin
      if not Assigned(ThreadA) then
        ThreadA := TThreadA.Create;
    end;
    Mais si c'est vraiment juste une pause que tu souhaites il faudra jouer sur un mutex.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    procedure TThreadA.Execute;
    begin
      while not Terminated do
      begin
        RtlEventWaitFor(WaitRunning);
        ...
      end;
    end;
    procedure TThreadB.Execute;
    begin
      while not Terminated do
      begin
        RtlEventWaitFor(WaitRunning);
        ...
      end;
    end;
     
    procedure TForm1.btnStartClick(Sender: TObject);
    begin
      RTLEventSetEvent(WaitRunning);
    end;
     
    procedure TForm1.btnStopClick(Sender: TObject);
    begin
      RTLEventResetEvent(WaitRunning);
    end;
    Tu remarqueras que les deux threads sont conditionnés par le même mutex.

  5. #5
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 089
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 089
    Par défaut
    Bonjour, et merci pour tes explications.

    Citation Envoyé par Andnotor Voir le message
    Mais si c'est vraiment juste une pause que tu souhaites il faudra jouer sur un mutex.
    Tu lis dans mes pensées, ou quoi ?
    Bref, j'ai tout bien mis en place enfin, je crois :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    ...
    var
      Form1: TForm1; 
      WaitRunning: PRtlEvent;
     
    implementation
    ...
    mais quelque chose coince :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure TForm1.btnStartClick(Sender: TObject);
    begin
      RTLEventSetEvent(WaitRunning);
      if ThreadA = nil then ThreadA := TThreadA.Create(false);
    end;
    Au clic, ça part en boucle folle et pour reprendre la main c'est tendu et laborieux (heureusement qu'on a des consoles sous Linux !)!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure TForm1.btnStartClick(Sender: TObject);
    begin
      if ThreadA = nil then ThreadA := TThreadA.Create(false);
      RTLEventSetEvent(WaitRunning);
    end;
    Pareil.


    Après, je ne sais pas trop où regarder ni quoi :
    Citation Envoyé par Andnotor Voir le message
    C'est également Terminated du Thread A ou B ? sur lequel il faut boucler, pas celui de TApplication.

    Si ThreadB est bien déclaré au niveau de TThreadA et que la boucle est conditionnée par Terminated du Thread A ou B ?, il suffit d'un Free (FreeAndNil) pour terminer/libérer l'ensemble.

    C'est également Terminated du Thread A ou B ? sur lequel il faut boucler, pas celui de TApplication.
    Merci,

  6. #6
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 912
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 912
    Par défaut
    Citation Envoyé par Jipété Voir le message
    Au clic, ça part en boucle folle
    Ce n'est pas différent de ton premier exemple, mais ici jusqu'à ce que tu cliques le bouton Stop.

    Quel est le but de tout cela précisément ?

  7. #7
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 089
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 089
    Par défaut
    Yep !

    j'ai donc mis en place ta nouvelle proc FormDestroy, F9 (plus de démarrage automatique), je compte jusqu'à 5, je clique sur la croix et bam !

    Nom : no_réponse.png
Affichages : 258
Taille : 7,7 Ko

    suivi de l'habituel SIGTERM... Chez toi ça fonctionne bien ?

    Sinon,

    Nom : form1_propre.png
Affichages : 266
Taille : 21,8 Ko

    Pas d'idée pour les deux sauts de ligne quand je n'en demande qu'un ?
    Si je commente ma ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
          PostMessage(Wnd, LM_LOG, thB_Counter, ThreadB.Counter);
          PostMessage(Wnd, LM_LOG, thB_LineJump, 0); // <<<<<<<<<< elle !
          // B terminé !
    , ça donne ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    thA: wait for thB ...
    thB: Working ... ... ... ... ... ...
    thB: Wake thA
    thA: ThreadB.Counter=1
    thA: wait for thB ...
    thB: Working ... ... ... ... ... ...
    thB: Wake thA
    thA: ThreadB.Counter=2
    thA: wait for thB ...
    thB: Working ... ... ... ... ... ...
    thB: Wake thA
    thA: ThreadB.Counter=3
    et ce thB_LineJump c'est juste memo1.Lines.Add(CRLF); et CRLF un grand classique, const CRLF = #13#10;.
    Je reste un peu sans voix et sans voie pour avancer, là.

    EDIT : Ah, Jérôme, salustre ! Je n'ai pas vu ta réponse quand j'ai préparé la mienne, je l'étudie, please waitfor...

  8. #8
    Membre Expert
    Avatar de BeanzMaster
    Homme Profil pro
    Amateur Passionné
    Inscrit en
    Septembre 2015
    Messages
    1 899
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Amateur Passionné
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Septembre 2015
    Messages : 1 899
    Billets dans le blog
    2
    Par défaut
    La seule fois ou j'ai réussis a ce que cela fonctionne sous Windows et Lazarus linux c'était pour un timer dans un thread https://github.com/jdelauney/BZScene...hreadTimer.pas Ca pourra peut-être t'aider a te dépatouiller
    • "L'Homme devrait mettre autant d'ardeur à simplifier sa vie qu'il met à la compliquer" - Henri Bergson
    • "Bien des livres auraient été plus clairs s'ils n'avaient pas voulu être si clairs" - Emmanuel Kant
    • "La simplicité est la sophistication suprême" - Léonard De Vinci
    • "Ce qui est facile à comprendre ou à faire pour toi, ne l'est pas forcément pour l'autre." - Mon pèrei

    Mes projets sur Github - Blog - Site DVP

  9. #9
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 912
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 912
    Par défaut
    Certainement ThreadB encore en attente.

    Dans ThreadA, ajoute :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    finally
      RTLEventSetEvent(WaitRunning);
      ThreadB.Free;
    end;
    Et pour arrêter plus vite ThreadB :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    for i := 1 to 5 do begin
      if Terminated the Break;
      Sleep(300);
      PostMessage(Wnd, LM_LOG, B_Running, 0);
    end;
    Citation Envoyé par Jipété Voir le message
    suivi de l'habituel SIGTERM... Chez toi ça fonctionne bien ?
    SIGTERM je connais pas mais est-ce vraiment une erreur ou juste un signal de fin ?

    Ca risque pas de fonctionner chez moi puisque... je n'ai pas Lazarus


    Pour les double lignes essaye juste un #13.

  10. #10
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 912
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 912
    Par défaut
    Ce qui est surprenant est que ThreadB n'appartienne pas à ThreadA mais à Form1

    Citation Envoyé par Jipété Voir le message
    Est-ce à dire que si je fais tourner le programme un certain temps, ça va consommer toute la mémoire disponible jusqu'au crash de la machine ?
    Pas de FreeOnTerminate dans TThreadB ?

    Sinon ça va dépendre de l'utilisation de TThreadA. Si cette tâche n'est créée qu'une fois au démarrage, il n'y aura aussi qu'un ThreadB.


    Mais l'ensemble ne semble pas très cohérent.

  11. #11
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 089
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 089
    Par défaut
    Bonsoir,

    à pas d'heure, une bonne nouvelle : il n'y a plus de plantage à l'arrêt, et je ne sais pas trop pourquoi, m'étant juste contenté de rajouter un message (de saut de ligne pour une meilleure lisibilité) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const
      ...
      thB_LineJump = 6; // new
     
      with Memo1 do begin
        case Message.WParam of
          ...
          thB_LineJump : Lines.Add(CRLF); // new
        end;
        SelStart := Length(Text); // scroll auto pour toujours voir le bas du mémo
      end;
     
    procedure TThreadA.Execute;
      ...
          PostMessage(Wnd, LM_LOG, thB_Counter, ThreadB.Counter);
          PostMessage(Wnd, LM_LOG, thB_LineJump, 0); // new
          // B terminé !
        end;
    Et ça donne ça, avec 2 lignes (pourquoi 2 ? Je n'insère qu'un seul CRLF) entre chaque bloc :

    Nom : fonctionne_à-peu-près-bien.png
Affichages : 263
Taille : 42,6 Ko

    EDIT : perdu, le plantage en clôture est aléatoire... On dirait que le code n'est pas propre, qu'il doit rester des scories en mémoire, des fois, ou quand le ménage est mal fait et qu'une instruction se prend les pieds dans le tapis, parce qu'elle n'aurait pas dû être appelée à ce moment-là ou que le tapis n'a pas été rangé -- ça fait penser à ça /EDIT
    Bon, j'ai remarqué que la clôture n'était pas instantanée aussi, quand elle fonctionnait sans anicroches.


    Ah, une autre question, puisque j'ai le nez dessus : dans ThreadB.Execute, je lis
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        ...
        PostMessage(Wnd, LM_LOG, thB_Working, 0);
        for i := 1 to 5 do begin
          Sleep(300);
          PostMessage(Wnd, LM_LOG, thB_Running, 0);
        end;
        Counter := Counter + 1;
        // Signale la fin
        PostMessage(Wnd, LM_LOG, thB_WakeA, 1);
        RTLEventSetEvent(WaitFinish);
        ...
    À quoi servent/correspondent ces "0" et ce "1" en 4e paramètre des PostMessage ?

    Et la dernière, pour la route :
    dans btnStartClick, j'ai juste LeaveCriticalSection(csPause); et au début de ThreadB.Execute je lis
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      while not Terminated do
      begin
        // Attend Start
        RtlEventWaitFor(WaitRunning);
    Vu comme ça, on dirait que RTLEventWaitFor est un truc qui tourne en boucle en attendant qu'il se passe quelque chose quelque part, alors, concrètement, que fait ce LeaveCriticalSection ? Il passe un flag, modifie un boolean caché à nous ou autre mécanisme similaire, à charge pour l'event de faire ce qu'il faut à réception, ie sortir de sa boucle ?

    Merci infiniment, on a déjà bien avancé, je trouve.

  12. #12
    Membre Expert
    Avatar de BeanzMaster
    Homme Profil pro
    Amateur Passionné
    Inscrit en
    Septembre 2015
    Messages
    1 899
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Amateur Passionné
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Septembre 2015
    Messages : 1 899
    Billets dans le blog
    2
    Par défaut
    Hello un vrai plaie ces rtlEvent avec le thread main, je me souviens je voulais faire un truc dans le genre et du coup, j'avais rusé en utilisant les sections critique et synchronize et y'a un thread TWaitFor (qui ne sert à rien ici, mais je ne me rappel plus pourquoi, ni comment je voulais l'utiliser pour attendre). Bref une ruse de sioux. Je n'ai jamais résussis a ce que cela fonctionne correctement avec les RTLEvent à l'époque

    PS: avec RTLWaitForEvent tu peux mettre un timeout en second paramètre

    Je vous met le source d'un de mes tests que j'ai retrouvé. Il permet de lancer plusieurs thread en parallèle en même temps si cela peux t'aider.
    La form rien de compliqué 3 progressbar et un bouton (j'ai laissé tous mes commentaires )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    Unit Unit1;
     
    {$mode objfpc}{$H+}
     
    Interface
     
    Uses
      Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls;
     
    Const
      cWaitOutTime  = 500;
     
    Type
     
      TBZTaskProcessMethod = procedure (Sender : TObject) of object;
      TBZTaskProcessParallelMethod = procedure(Sender : TObject; Index : Integer) of object;
     
    //Type
    //  TBZCustomThreadManager = Class;
    //  TBZCustomThreadTaskList  = Class;
     
    {%region%=====[ TASKS ]=======================================================================}
     
      TBZCustomThreadTask = Class(TObject) 
      private
       // FOwner : TBZCustomThreadManager;
        FName : String;
     
        FData : TObject;
        FAutoFreeData : Boolean;
     
        FTaskPriority : Integer;
        //FOwnerThread : TBZCustomThread;
        //FThreadID : Integer;
     
        procedure SetData(AValue : TObject);
       // procedure SetOwnerThread(AValue : TBZCustomThread);
        procedure SetTaskPriority(AValue : Integer);
      public
        // constructor Create(AOwner : TBZCustomThreadManager; Const AName : String =''; Const APriority : Integer = 0); overload;
        constructor Create(Const AName : String =''; Const APriority : Integer = 0); overload;
        destructor Destroy; override;
     
        procedure Process; Virtual; Abstract;
     
        //property OwnerThread : TBZCustomThread read FOwnerThread write SetOwnerThread;
        //property Owner: TBZCustomThreadManager read FOwner write FOwner;
        property Name: string read FName write FName;
        property TaskPriority : Integer read FTaskPriority write SetTaskPriority;
        property Data : TObject read FData write SetData;
        property AutoFreeData : Boolean read FAutoFreeData write FAutoFreeData;
      end;
     
      TBZThreadTaskClasses = Class of TBZCustomThreadTask;
     
      { TBZBaseThreadTask }
     
      TBZBaseThreadTask = Class(TBZCustomThreadTask)
      private
        FTaskProc :   TBZTaskProcessMethod;
      protected
        procedure Process; override;
        property TaskProc: TBZTaskProcessMethod read FTaskProc write FTaskProc;
      end;
     
      TBZCustomParallelTask = Class(TBZCustomThreadTask)
      private
        FIndex : Int64;
         //FStartOffset
         //FEndOffset;
        //FMaxIteration
        FTaskProc :  TBZTaskProcessParallelMethod;
      public
        constructor Create(Const AName : String =''; Const APriority : Integer = 0); //Override;
        procedure Process; Override;
     
        property Index : Int64 Read FIndex write FIndex;
        property TaskProc: TBZTaskProcessParallelMethod read FTaskProc write FTaskProc;
      end;
     
      TBZBaseParallelTask = Class(TBZCustomParallelTask);
     
    {%region%=====[ THREADS ]=====================================================================}
     
      TBZWaitForThread = Class(TThread);
     
      TBZThreadState = ( tsInitializing, tsWaiting, tsRunning, tsPaused, tsCompleted, tsTerminated);
      TBZCustomThread = Class(TThread) //IProgressable
      private
        //FOwner: TBZCustomThreadManager;
        //FRunning : Boolean;
        //FCompleted : Boolean;
        FTask : TBZCustomThreadTask;
        FOffset : Integer;
     
        FTaskName : String;
        FTaskIndex,
        // Ou FTask : TBZCustomThreadTask; ??????
        FThreadIndex : Integer;
     
        FState : TBZThreadState;
        //FWaitingFor : Boolean;
     
        //FOnStart;
        //FOnPause;
        //FOnCompleted;
     
        //procedure SetCompleted(AValue : Boolean);
        //procedure SetWaitingFor(AValue : Boolean);
        //procedure SetRunning(AValue : Boolean);
        procedure SetTaskName(AValue : String);
     
        procedure NotifyThreadManager;
      protected
        FSuccess : Boolean;
     
        procedure InitTask; Virtual;
        procedure DoneTask; Virtual;
        procedure ProcessTask; Virtual; Abstract;
     
        //procedure TraceLog; virtual;
        procedure Execute; override; //virtual;
      public
        // OnTraceLog : TBZThreadTraceLogNotifyEvent;
        // OnProgress
     
        constructor Create(Const isSuspended : Boolean = False);
     
       // procedure Execute; override; //virtual;
        procedure Run;
        procedure Stop;
        procedure Pause;
     
        //property Running : Boolean read FRunning write SetRunning;
        //property Completed : Boolean read FCompleted write SetCompleted;
        //property WaitingFor : Boolean read FWaitingFor write SetWaitingFor;
     
        property State : TBZThreadState read FState write FState;
       // property Owner :  TBZCustomThreadManager read FOwner;
        property TaskName : String read FTaskName write SetTaskName;
        property TaskIndex : Integer read FTaskIndex write FTaskIndex;
        property Task : TBZCustomThreadTask read FTask write FTask;
     
      end;
     
      { TBZBaseThread }
     
      TBZBaseThread = Class(TBZCustomThread)
      protected
        procedure ProcessTask; override; //Virtual;
      end;
     
    {%endregion%}
     
    Type
     
      { TForm1 }
     
      TForm1 = Class(TForm)
        Button1: TButton;
        ProgressBar1: TProgressBar;
        ProgressBar2: TProgressBar;
        ProgressBar3: TProgressBar;
        Procedure Button1Click(Sender: TObject);
      private
     
      public
        Task1, Task2, Task3 : TBZBaseThreadTask;
        Thread1, Thread2, Thread3 : TBZBaseThread;
     
        procedure UpdateProgressBar1(Sender:TObject);
        procedure UpdateProgressBar2(Sender:TObject);
        procedure UpdateProgressBar3(Sender:TObject);
      End;
     
    Var
      Form1: TForm1;
     
    Implementation
     
    {$R *.lfm}
     
    { TForm1 }
     
    Procedure TForm1.Button1Click(Sender: TObject);
    Begin
      ProgressBar1.Position := 0;
      ProgressBar2.Position := 0;
      ProgressBar3.Position := 0;
     
      Task1 := TBZBaseThreadTask.Create('Task1',0);
      Task2 := TBZBaseThreadTask.Create('Task2',0);
      Task3 := TBZBaseThreadTask.Create('Task3',0);
      Task1.TaskProc := @UpdateProgressBar1;
      Task2.TaskProc := @UpdateProgressBar2;
      Task3.TaskProc := @UpdateProgressBar3;
     
      Thread1 := TBZBaseThread.Create(True);
      Thread2 := TBZBaseThread.Create(True);
      Thread3 := TBZBaseThread.Create(True);
      Thread1.Task := Task1;
      Thread2.Task := Task2;
      Thread3.Task := Task3;
     
      Thread1.Run;
      Thread2.Run;
      Thread3.Run;
    end;
     
    Procedure TForm1.UpdateProgressBar1(Sender: TObject);
    Begin
      if ProgressBar1.Position < 100 then
      begin
        ProgressBar1.Position := ProgressBar1.Position + 10;
        Sleep(100);
        Application.ProcessMessages;
      end
      else
      begin
          Thread1.State := tsCompleted;
          Thread1.Terminate;
    //    TBZBaseThreadTask(Sender).Completed := True;
    //    UpdateUI;
      end;
    End;
     
    Procedure TForm1.UpdateProgressBar2(Sender: TObject);
    Begin
      if ProgressBar2.Position < 100 then
      begin
        ProgressBar2.Position := ProgressBar2.Position + 5;
        Sleep(50);
        Application.ProcessMessages;
      end
      else
      begin
           Thread2.State := tsCompleted;
           Thread2.Terminate;
    //    TBZBaseThreadTask(Sender).Completed := True;
    //    UpdateUI;
      end;
    End;
     
    Procedure TForm1.UpdateProgressBar3(Sender: TObject);
    Begin
      if ProgressBar3.Position < 100 then
      begin
        ProgressBar3.Position := ProgressBar3.Position + 1;
        Sleep(20);
        Application.ProcessMessages;
      end
      else
      begin
          Thread3.State := tsCompleted;
          Thread3.Terminate;
    //    TBZBaseThreadTask(Sender).Completed := True;
    //    UpdateUI;
      end;
    End;
     
    {%region%=====[ TBZCustomThreadTask ]=======================================================}
     
    procedure TBZCustomThreadTask.SetData(AValue : TObject);
    begin
      if FData = AValue then Exit;
      FData := AValue;
    end;
     
    //procedure TBZCustomThreadTask.SetOwnerThread(AValue : TBZCustomThread);
    //begin
    //  if FOwnerThread = AValue then Exit;
    //  FOwnerThread := AValue;
    //end;
     
    procedure TBZCustomThreadTask.SetTaskPriority(AValue : Integer);
    begin
      if FTaskPriority = AValue then Exit;
      FTaskPriority := AValue;
    end;
     
    constructor TBZCustomThreadTask.Create(Const AName : String; Const APriority : Integer);
    begin
      Inherited Create;
     // FOwner := AOwner;
      FName := AName;
      FData := nil;
      FTaskPriority := APriority;
      //FOwnerThread := nil;
    end;
     
    destructor TBZCustomThreadTask.Destroy;
    begin
      if (FData<>nil) and FAutoFreeData then FreeAndNil(FData);
      inherited;
    end;
     
    {%endregion%}
     
     
    procedure TBZBaseThreadTask.Process;
    begin
      if Assigned(FTaskProc) then FTaskProc(Self);
    end;
     
    {%region%=====[ TBZCustomParallelTask ]=====================================================}
     
    constructor TBZCustomParallelTask.Create( Const AName : String; Const APriority : Integer);
    begin
      inherited Create(AName,APriority);
      FIndex := -1;
    end;
     
    procedure TBZCustomParallelTask.Process;
    begin
      if Assigned(FTaskProc) then FTaskProc(Self, FIndex);
    end;
     
    {%endregion%}
     
    {%region%=====[ TBZCustomThread ]===========================================================}
     
    procedure TBZCustomThread.SetTaskName(AValue : String);
    begin
      if FTaskName = AValue then Exit;
      FTaskName := AValue;
    end;
     
    procedure TBZCustomThread.NotifyThreadManager;
    begin
      case FState of
         tsInitializing,
         tsWaiting,
         tsRunning,
         tsPaused,
         tsCompleted,
         tsTerminated : ;
      end;
    end;
     
    procedure TBZCustomThread.InitTask;
    begin
      // FOnStartTask
      // Do nothing here
    end;
     
    procedure TBZCustomThread.DoneTask;
    begin
      // FOnDoneTask
      // Do nothing here
    end;
     
    constructor TBZCustomThread.Create( Const isSuspended : Boolean);
    begin
      //FOwner := AOwner;
      FreeOnTerminate := True  ;
      inherited Create(isSuspended);
    end;
     
    procedure TBZCustomThread.Run;
    begin
      State := tsRunning;
      //Self.Resume;
      Self.Suspended := False;
    end;
     
    procedure TBZCustomThread.Stop;
    begin
      Self.Terminate;
      State := tsTerminated;
    end;
     
    procedure TBZCustomThread.Pause;
    begin
      State := tsPaused;
      Self.Suspended := true;
    end;
     
    procedure TBZCustomThread.Execute;
    begin
      try
        InitTask;
        try
          ProcessTask;
        except
          //Handle Exception --> to be done in future
        end;
        DoneTask;
      finally
        NotifyThreadManager;
      end;
    end;
     
    {%endregion%}
     
    {%region%=====[ TBZBaseThread ]=============================================================}
     
    procedure TBZBaseThread.ProcessTask;
    var
      aTask : TBZCustomThreadTask;
    begin
      while not Terminated do
      begin
        //if FOwner.Running then
        //begin
         // aTask := FOwner.GetTask;
          //if Assigned(FTask) then
          //begin
            //FTaskName := aTask.Name;
            //FRunning := True;
            // FTask := aTask;
            if State = tsRunning then
           // try
              Synchronize(@FTask.Process);
            //finally
            //  FTaskName := '';
            //  State := tsCompleted;
              //FreeAndNil(aTask); //.Free;
           // end;
          //end
          //else
          //  State := tsTerminated;
       // end;
       // Sleep(cWaitOutTime);
      end;
      State := tsTerminated;
      ShowMessage(Ftask.Name + ' Terminated');
    end;
     
    {%endregion%}
     
     
    End.
    Dans un autre code j'ai retrouvé ca

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    procedure TBZBaseThread.ProcessTask;
    var
      aTask : TBZCustomThreadTask;
    begin
      if FOwner.Running then
      begin
        //while not Terminated do
        //begin
        aTask := FOwner.GetTask;
        if Assigned(aTask) then // and not(aTask.Completed)
        begin
          FTaskName := aTask.Name;
          State := tsRunning;
          while (State = tsRunning) do
          begin
            // Try
             if Assigned(FWaitForTask) then
             begin
                //property OnWaitForTask : TNotifyEvent read FOnWaitForTask write FOnWaitForTask
               if Assigned(FOnWaitForTask) then FOnWaitForTask(Self);
               //aTask.ProcessWaitFor;
             end
             else
             begin
               if aTask.SynchonizeTask then
               begin
                  if Assigned(FOnSynchronizeTask) then Synchronize(@FOnSynchronizeTask(Self));
                  Synchronize(@aTask.Process);
                end
                else
                begin
                  aTask.Process;
                end;
             // finally
                //FTaskName := '';
               if aTask.Completed then
               begin
                  State := tsCompleted;
                  NotifyThreadManager;
                  Stop;
                  aTask.Completed := true;
                end;
     
               if Assigned(FWaKeUpTask) then
               begin
                 if Assigned(FOnWakeUpTask) then FOnWakeUpTask(Self);
                 //aTask.ProcessWakeUp;
               end;
     
            end;
          end;
        end
        else
        begin
          State := tsTerminated;
        end;
      end
      else
      begin
        State := tsTerminated;
      end;
     
      // Sleep(cWaitOutTime);
    end;
    A+
    Jérôme
    • "L'Homme devrait mettre autant d'ardeur à simplifier sa vie qu'il met à la compliquer" - Henri Bergson
    • "Bien des livres auraient été plus clairs s'ils n'avaient pas voulu être si clairs" - Emmanuel Kant
    • "La simplicité est la sophistication suprême" - Léonard De Vinci
    • "Ce qui est facile à comprendre ou à faire pour toi, ne l'est pas forcément pour l'autre." - Mon pèrei

    Mes projets sur Github - Blog - Site DVP

  13. #13
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 089
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 089
    Par défaut
    Bon, j'arrive à le faire tourner sans plantages ni AV, mais pas moyen de l'arrêter proprement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      InitCriticalSection(ACriticalSection);
    end;
     
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      DoneCriticalsection(ACriticalSection);
      if ThreadA <> nil then ThreadA.Terminate;
      //if ThreadB <> nil then ThreadB.Terminate;
    end;
     
    procedure TForm1.btnStartClick(Sender: TObject);
    begin // dans un décommentage comme dans l'autre, ça ne va pas...
    //  RTLEventSetEvent(ThreadA.WaitRunning);
      if ThreadA = nil then ThreadA := TThreadA.Create(false);
    //  RTLEventSetEvent(ThreadA.WaitRunning);
    end;
     
    procedure TForm1.btnStopClick(Sender: TObject);
    begin
      RTLEventResetEvent(ThreadA.WaitRunning);
      Memo1.SetFocus; // juste pour vérifier que le prog n'est pas planté
    end;
     
    procedure TThreadA.Execute;
    var
      ThreadB :TThreadB;
    begin
      ThreadB := TThreadB.Create(false); 
      WaitRunning := RTLEventCreate;
     
    //  while not Application.Terminated do begin
      while not Terminated do begin
        Application.ProcessMessages;
        //RtlEventWaitFor(WaitRunning);  mis en bas
        Log('A: wait for B ...');
        // wait infinitely (until B réveille A)
        Log('A: ThreadB.Counter=' + IntToStr(ThreadB.Counter)); 
        RtlEventWaitFor(WaitRunning);  //mis en bas
      end;
    end;
     
    procedure TThreadB.Execute;
    var  i: Integer;
    begin
      Counter := 0;
    //  while not Application.Terminated do begin
      while not Terminated do begin
        Application.ProcessMessages;
        //RtlEventWaitFor(Form1.ThreadA.WaitRunning);
        Log('B: Working ...');
        for i := 1 to 5 do begin
          Sleep(300);
          Log('.....  ', False);
        end;
        Log('');
        Counter := Counter + 1;
        Log('B: Wake A');
        // wake A
        RtlEventSetEvent(Form1.ThreadA.WaitRunning);
      end; // while
    end;
    // Ce qui est surprenant est que ThreadB n'appartienne pas à ThreadA mais à Form1  Andnotor
     
    procedure TForm1.AddMessage;
    begin
      Memo1.Lines.Text := MsgText;
      Memo1.SelStart := length(Memo1.Text);
    end;
     
    procedure TThreadForLog.Log(const Msg: string; AppendLineEnd: boolean);
    var
      s: String;
    begin
      EnterCriticalsection(Form1.ACriticalSection);
      s := Msg;
      if AppendLineEnd then s := s + LineEnding;
      //dbgout(s);
      Form1.MsgText := Form1.MsgText + s;
      Synchronize(@Form1.AddMessage);
      LeaveCriticalsection(Form1.ACriticalSection);
    end;
     
    end.
    Allez, à table, que j'ai faim !

  14. #14
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 912
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 912
    Par défaut
    1. Application.ProcessMessages dans un thread, tu oublies !
    2. Le mutex est systématiquement signalé à chaque boucle de ThreadB, le bouton Stop ne sert plus à rien.

    Pour synchroniser ThreadB avec ThreadA, ben il faut un deuxième mutex. Sur le principe, comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    var
      WaitRunning :PRTLEvent;
      WaitFinish  :PRTLEvent;
     
    procedure TForm1.bStartClick(Sender: TObject);
    begin
      RTLEventSetEvent(WaitRunning);
    end;
     
    procedure TForm1.bStopClick(Sender: TObject);
    begin
      RTLEventResetEvent(WaitRunning);
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      ThreadA := TThreadA.Create;
    end;
     
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      RTLEventSetEvent(WaitRunning);
      ThreadA.Free;
    end;
     
    { TThreadA }
     
    procedure TThreadA.Execute;
    var
      ThreadB :TThreadB;
    begin
      ThreadB := TThreadB.Create;
     
      try
        while not Terminated do
        begin
          // Prépare B
          RTLEventResetEvent(WaitFinish);
          // Attend Start
          RtlEventWaitFor(WaitRunning);
     
          ...
     
          // Attend la fin de B
          RtlEventWaitFor(WaitFinish);
     
          // B terminé !
        end;
     
      finally
        ThreadB.Free;
      end;
    end;
     
    { TThreadB }
     
    procedure TThreadB.Execute;
    begin
      while not Terminated do
      begin
        // Attend Start
        RtlEventWaitFor(WaitRunning);
     
        ...
     
        // Signale la fin
        RTLEventSetEvent(ThreadB.WaitFinish);
      end;
    end;
     
    initialization
      WaitRunning := RTLEventCreate;
      WaitFinish  := RTLEventCreate;
     
    finalization
      RTLEventDestroy(WaitRunning);
      RTLEventDestroy(WaitFinish);

    La où je parlais d'incohérence précédemment est le fait de mettre en route deux tâches alors que la première (A) ne fait que lancer la deuxième et attendre. ThreadA ne sert à rien !

    Citation Envoyé par Jipété Voir le message
    Ce que je veux au final, c'est écouter des chansons et pouvoir appuyer sur Pause comme on le faisait sur les magnétophones à k7 pour arrêter la lecture et la reprendre plus tard.
    Et quelle est l'utilité des threads dans ce cas ?

  15. #15
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 089
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 089
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    1. Application.ProcessMessages dans un thread, tu oublies !
    2. Le mutex est systématiquement signalé à chaque boucle de ThreadB, le bouton Stop ne sert plus à rien.
    1. : oui, je m'en suis rendu compte.
    2. : bon, je vais revoir tout ça en tenant compte de tes précieux conseils.

    Citation Envoyé par Andnotor Voir le message
    Et quelle est l'utilité des threads dans ce cas ?
    Ben, pendant que le thread secondaire se tape le boulot de jouer le son, le thread principal (l'ihm) affichera la forme d'onde par exemple, la liste des morceaux et gèrera les boutons suivant, précédent, arrêter, etc.
    On peut sans doute faire autrement, mais c'est plus pour le fun qu'autre chose, pi j'ai déjà une vieille version de ça, sans threads1 mais avec des TFrames (ce qui revient un peu au même), qui est une abomination à faire fonctionner et à maintenir,
    ---
    1 : c'est pas vrai, il y en a un, mais je n'ai rien écrit, c'est l'unité u_monitorevents.pas, trouvée sur le web et qui me sert pour compenser un manque de FreePascal/Lazarus qui existe en Delphi (si ma mémoire est bonne), je veux parler des événements BeforeResize et AfterResize de TForm.

  16. #16
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 089
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 089
    Par défaut
    Andnotor, j'ai fait plein d'essais dans la proc FormDestroy en mode pas-à-pas et au bout du compte j'en suis là (lire tout en bas) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      //LeaveCriticalSection(csPause); //(anciennement csRunning) -- bloc try-finally mieux d'après l'aide
      Try
        // Code to be protected goes here.
      Finally
        LeaveCriticalSection(csPause); // libère le verrou posé par FormCreate ou par btnStop
      end;
      // jurassicpork :
      ////andnotor pas d'accord if ThreadA <> nil then ThreadA.Terminate;
     
      if not ThreadA.Finished then begin // ajouté
    //  ThreadA.Terminate; enlevé car fait aussi dans WaitFor d'après l'aide
      // c'est la ligne dessous qui après quelques secondes génère la petite fenêtre 
      // d'appli qui ne répond pas puis une autre avec le sigterm
      ThreadA.WaitFor; // Waits for the thread to terminate and returns the exit status. dixit l'aide
      ThreadA.Free;
      end;
    end;
    Une idée ?

    EDIT : autre essai
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
      ...
      if not ThreadA.Finished then begin
        ThreadA.Terminate; // présente ou absente ça ne change rien
        DoneThread; {DoneThread should be used to end the current thread.
        It performs the necessary housekeeping before actually ending the thread.
        Using the operating system calls to end the thread may result
        in data corruption or memory leaks.}
      //  ThreadA.WaitFor; // Waits for the thread to terminate and returns the exit status.
      //  ThreadA.Free;
      end;
    end;
    // c'est ici que ça génère un sigterm
    Si je remets ThreadA.Free c'est cette ligne qui plante, si je remets avec .WaitFor c'est elle qui plante.
    C'est épuisant...

  17. #17
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 912
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 912
    Par défaut
    Ah là là ! Qu'est-ce qui n'allait pas avec mon code précédent ?

    Try..finally n'a aucun intérêt. On est pas en train de traiter un bloc de code.
    ThreadA.Finished, aucun intérêt non plus. On sait que le thread est en cours.
    DoneThread de la tâche principale ? Euh... non ! (mais je ne connais pas cette commande)
    ThreadA.WaitFor attend la fin du thread (la sortie de Execute) mais ThreadA.Terminate est nécessaire pour sortir de sa boucle. A mois d'une subtilité Free Pascal, WaitFor ne force pas Terminate.

    Waits for the thread to terminate n'est pas égale à requests the thread to terminate and wait.

  18. #18
    Expert confirmé
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    11 089
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 089
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Ah là là ! Qu'est-ce qui n'allait pas avec mon code précédent ?
    Ben les plantages à la clôture, avec SIGSEGV ou SIGTERM.

    Citation Envoyé par Andnotor Voir le message
    Try..finally n'a aucun intérêt. On est pas en train de traiter un bloc de code.
    Ça vient de l'aide, ou du wiki, je sais plus trop. En tout cas on retrouve ce bloc dans la page que je cite plus bas, dans la section Les sections critiques presque tout en bas.

    Citation Envoyé par Andnotor Voir le message
    DoneThread de la tâche principale ? Euh... non ! (mais je ne connais pas cette commande)
    Dans les lignes des threads c'est pas mieux. Et je me suis douté que certains ne connaitraient pas la commande, c'est pour ça que je l'ai fait suivre des lignes d'aide. Ça avait l'air sympa.

    Citation Envoyé par Andnotor Voir le message
    ThreadA.WaitFor attend la fin du thread (la sortie de Execute) mais ThreadA.Terminate est nécessaire pour sortir de sa boucle. A moins d'une subtilité Free Pascal, WaitFor ne force pas Terminate.
    Comment expliquer que la ligne fasse planter, alors ?

    Bon, de toute façon, une page (https://wiki.freepascal.org/Multithr...on_Tutorial/fr) me conseille d'utiliser l'exemple SingleThread plutôt que l'exemple WaitFor, je sens que je vais suivre cette idée, dès que je l'aurais débuggué puisque lui aussi, comment dire, il propose un bouton à trois états juste impossible à atteindre...

    C'est lassant, disais-je.
    Et j'ai plein d'autres choses à faire cet aprème,

  19. #19
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 912
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 912
    Par défaut
    Citation Envoyé par Jipété Voir le message
    Ça vient de l'aide, ou du wiki
    Oui mais on est pas dans ce cas de figure. C'est ainsi qu'il aurait fallu écrire la partie log par exemple.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    EnterCriticalsection(Form1.ACriticalSection);
    try
      s := Msg;
      if AppendLineEnd then s := s + LineEnding;
      //dbgout(s);
      Form1.MsgText := Form1.MsgText + s;
      Synchronize(@Form1.AddMessage);
    finally
      LeaveCriticalsection(Form1.ACriticalSection);
    end;

    Ca ne va pas aider mais j'ai fais un essai Delphi pour m'assurer qu'il n'y avait pas de blocage ou VA et rien à signaler.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    unit Unit6;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
     
    const
      LM_LOG = WM_USER;
     
    type
      TThreadA = class(TThread)
      protected
        procedure Execute; override;
      end;
     
      TThreadB = class(TThread)
      protected
        procedure Execute; override;
      public
        Counter :integer;
      end;
     
      TForm6 = class(TForm)
        Memo1: TMemo;
        bStart: TButton;
        bStop: TButton;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure bStartClick(Sender: TObject);
        procedure bStopClick(Sender: TObject);
      private
        ThreadA :TThreadA;
        procedure LMLog(var Message :TMessage); message LM_LOG;
      end;
     
    var
      Form6: TForm6;
     
    implementation
     
    uses System.SyncObjs;
     
    const
      A_WaitForB = 1;
      A_Counter  = 2;
      B_Working  = 3;
      B_Running  = 4;
      B_WakeA    = 5;
     
    var
      Wnd         : THandle;
      CSPause     : TCriticalSection;
      WaitRunning : TEvent;
      WaitFinish  : TEvent;
     
    {$R *.dfm}
     
    procedure TForm6.bStartClick(Sender: TObject);
    begin
      CSPause.Leave;
    end;
     
    procedure TForm6.bStopClick(Sender: TObject);
    begin
      CSPause.Enter;
    end;
     
    procedure TForm6.FormCreate(Sender: TObject);
    begin
      CSPause.Enter;
     
      Wnd     := Handle;
      ThreadA := TThreadA.Create(FALSE);
    end;
     
    procedure TForm6.FormDestroy(Sender: TObject);
    begin
      CSPause.Leave;
      ThreadA.Terminate;
      ThreadA.WaitFor;
      ThreadA.Free;
    end;
     
    procedure TForm6.LMLog(var Message: TMessage);
    begin
      case Message.WParam of
        A_WaitForB : Memo1.Lines.Add('A: wait for B ...');
        A_Counter  : Memo1.Lines.Add(Format('A: ThreadB.Counter=%d', [Message.LParam]));
        B_Working  : Memo1.Lines.Add('B: Working ...');
        B_Running  : Memo1.Lines[Memo1.Lines.Count -1] := Memo1.Lines[Memo1.Lines.Count -1] +' ...';
        B_WakeA    : Memo1.Lines.Add('B: Wake A');
      end;
    end;
     
    { TThreadA }
     
    procedure TThreadA.Execute;
    var
      ThreadB :TThreadB;
    begin
      ThreadB := TThreadB.Create(False);
     
      try
        while not Terminated do
        begin
          // Attend Start (mais ne conserve pas le verrou)
          CSPause.Enter;
          CSPause.Leave;
     
          WaitRunning.SetEvent;
     
          // Attend la fin de B
          PostMessage(Wnd, LM_LOG, A_WaitForB, 0);
          WaitFinish.WaitFor;
          PostMessage(Wnd, LM_LOG, A_Counter, ThreadB.Counter);
          // B terminé !
        end;
     
      finally
        WaitRunning.SetEvent;
        ThreadB.Free;
      end;
    end;
     
    { TThreadB }
     
    procedure TThreadB.Execute;
    var
      i: Integer;
    begin
      while not Terminated do
      begin
        // Attend Start
        WaitRunning.WaitFor;
     
        PostMessage(Wnd, LM_LOG, B_Working, 0);
        for i := 1 to 5 do 
        begin
          Sleep(300);
          PostMessage(Wnd, LM_LOG, B_Running, 0);
        end;
     
        Counter := Counter + 1;
     
        // Signale la fin
        PostMessage(Wnd, LM_LOG, B_WakeA, 1);
        WaitFinish.SetEvent;
      end;
    end;
     
    initialization
      CSPause := TCriticalSection.Create;
      WaitRunning := TEvent.Create(nil, FALSE, FALSE, '');
      WaitFinish  := TEvent.Create(nil, FALSE, FALSE, '');
     
    finalization
      CSPause.Free;
      WaitRunning.Free;
      WaitFinish.Free;
     
    end.
    Je crains ne pouvoir t'aider plus

  20. #20
    Membre Expert
    Avatar de BeanzMaster
    Homme Profil pro
    Amateur Passionné
    Inscrit en
    Septembre 2015
    Messages
    1 899
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Amateur Passionné
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Septembre 2015
    Messages : 1 899
    Billets dans le blog
    2
    Par défaut
    Le code mon dernier message est surement plus adapté a ce que tu veux faire et surtout beaucoup plus simple, il faudrait juste que tu adaptes la fonctions updateTimer (ligne 347) et que tu appel ta fonction qui doit exécuter tes actions à la place de l'appel de l'event FOnTimer (ligne 319) pour lui faire faire ce que tu veux dans le thread (TBZThreadTimer)
    • "L'Homme devrait mettre autant d'ardeur à simplifier sa vie qu'il met à la compliquer" - Henri Bergson
    • "Bien des livres auraient été plus clairs s'ils n'avaient pas voulu être si clairs" - Emmanuel Kant
    • "La simplicité est la sophistication suprême" - Léonard De Vinci
    • "Ce qui est facile à comprendre ou à faire pour toi, ne l'est pas forcément pour l'autre." - Mon pèrei

    Mes projets sur Github - Blog - Site DVP

Discussions similaires

  1. Réponses: 6
    Dernier message: 27/07/2017, 03h17
  2. Besoin d'éclaircissement sur les évenements
    Par franquis dans le forum jQuery
    Réponses: 2
    Dernier message: 24/12/2010, 09h55
  3. besoin d'aide sur les thread
    Par demonofshadow dans le forum Windows Forms
    Réponses: 8
    Dernier message: 14/01/2010, 15h07
  4. Besoin d'éclaircissement sur les sockets
    Par Delphy113 dans le forum Langage
    Réponses: 3
    Dernier message: 11/06/2008, 18h10
  5. [CR] besoin d'aide sur les formules
    Par GuillaumeDSA dans le forum Formules
    Réponses: 4
    Dernier message: 10/07/2003, 12h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo