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

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

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 102
    Par défaut
    Bon, je ne comprends plus rien à ce qui se passe avec les posts de dvlp qui disparaissent et réapparaissent selon leur bon vouloir : ma réponse de 12 h 35 fait ainsi référence à ton post du 28 où il y a 2 codes et on va dire qu'on s'en fiche sinon on ne s'en sortira pas.

    Donc ici et maintenant je continue sur ton code de 8 h 22 (qui n'était pas visible quand j'ai commencé mes essais et ma réponse) et voilà ce que je peux en dire :

    - concernant WaitFor, en Lazarus je suis obligé de mettre les parenthèses et d'y mettre une valeur dedans sinon ça ne compile pas.
    Premier essai avec que des "0", c'est plantage direct. Alors j'ai mis un "1" partout sauf là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for i := 1 to 5 do
       begin
         if Terminated or (WaitRunning.WaitFor(0) = wrTimeout) then Break;
    et ça a bien planté aussi, alors j'ai mis un "1" et je suis arrivé à ça :

    Nom : nouveau-code.jpg
Affichages : 317
Taille : 84,2 Ko

    Si dans l'image on voit mal l'ascenseur à droite c'est parce que j'ai bougé la fenêtre du message et comme l'appli dessous est plantée, elle ne rafraichit plus son ihm.

    Et on voit aussi que l'affichage est loin d'être parfait : le bloc du compteur à 1900 est ok, celui à 1901 a loupé une ligne, et les autres après c'est en vrac jusqu'au SIGABRT...

    Selon la valeur ça plante + ou - vite : 0 instantané, 1 ça va plus loin, 5 encore plus loin.
    Est-ce qu'il faut que je modifie les autres, au pif ? Que dit l'aide de Delphi ?

    - concernant tout le reste, je n'ai touché à rien et il n'y a même plus besoin de rajouter de saut de ligne : tu peux m'expliquer ce tour de magie ?

    C''est tout, bon app',

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 921
    Par défaut
    Si tu lis mes réponses en diagonale on va pas s'en sortir

    Ce sont des millisecondes et si WaitFor n'a pas de paramètre par défaut, tu mets INFINITE !

    Citation Envoyé par Jipété Voir le message
    je ne comprends pas que Delphi accepte ta version
    Je ne vois pas où tu as vu que je l'avais mis après, même si ça n'a pas grande importance où il est placé.

    Le problème est que tu ne réfléchis toujours pas multitâches. Tu crées une instance de TThreadA qui aura pour effet de demander à l'OS de planifier un nouveau thread. Mais d'ici qu'il démarre (qu'on entre dans Execute donc) on aura quitté FormCreate depuis longtemps ! Ca n'a rien d'instantané.

    Ne pas confondre l'instance de TThread qui est juste un banal objet et le thread proprement dit qui y est manipulé.

    Après ce que je te montre là est le concept du multi-threading. Dans un cas réel Wnd devrait être une variable de TThreadA (et TThreadB) assignée par son constructeur. Idem pour les events qui seraient totalement invisibles de l'extérieur de ces classes et manipulés par des méthodes Start/Stop.

    Je ne voulais juste pas ajouter de complexité avec de l'héritage et de l'encapsulation. C'est raté manifestement

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

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 102
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Si tu lis mes réponses en diagonale on va pas s'en sortir
    C'est à cause des posts qui disparaissent et réapparaissent comme par enchantement.

    Citation Envoyé par Andnotor Voir le message
    Je ne vois pas où tu as vu que je l'avais mis après, même si ça n'a pas grande importance où il est placé.
    Là, par exemple :
    Citation Envoyé par Andnotor Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      ThreadA := TThreadA.Create(False);
      Wnd := Handle;
    end;
    Quant à l'importance de sa place, sous Lazarus j'ai eu un plantage violent comme je n'en avais encore jamais vu dans ce projet...

    Citation Envoyé par Andnotor Voir le message
    Ce sont des millisecondes et si WaitFor n'a pas de paramètre par défaut, tu mets INFINITE !
    Ah, enfin une information précise !
    Quant à INFINITE, c'est sans doute le mot qui fait peur, en y ajoutant la notion de deadlock si rien ne vient décoincer l'attente : un TimeOut qui tourne ad vitam æternam n'est pas un timeout, stricto sensu, même si INFINITE n'est pas une valeur infinie,
    Citation Envoyé par lien Andnotor
    The calling thread will wait indefinitely when the constant INFINITE is specified for the TimeOut parameter.
    C'est faux, j'ai fait un test avec ShowMessage(inttostr(INFINITE)); et ça m'a affiché 4 294 967 296, soit 49 jours si j'ai bien converti (je passe ce qui suit la virgule)

    Ceci étant dit, avec du INFINITE partout, ça a tourné comme une horloge suisse, je l'ai arrêté au bout d'une heure, le compteur à 2207.
    Et maintenant je clique sur la croix et... suspense insoutenable... ça s'est bien terminé sans plantage !
    Juste une remarque : un clic sur la croix de fermeture met du temps à réagir (environ 1 seconde -- pas dramatique, juste curieux).

    Un grand, un énorme BRAVO à toi et à ta patience.

    J'attends un jour ou deux pour voir comment je vais gérer l'ajout d'un vrai bouton Pause pour que le bouton Stop fasse un vrai stop avec reset du compteur, pour le moment il fait plutôt Pause et le Start qui suit continue où c'était arrêté...

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 921
    Par défaut
    Tu chipotes encore !

    Citation Envoyé par Jipété Voir le message
    un TimeOut qui tourne ad vitam æternam n'est pas un timeout, stricto sensu
    WaitFor n'est pas un timeout et un WaitFor qui attend ad vitam æternam n'a pas de timeout.

    Mais c'est sûr qu'on pourrait la jouer shakespearienne : function THandleObject.WaitFor(TimeoutOrNotTimeout: Cardinal): TWaitResult;.


    Citation Envoyé par Jipété Voir le message
    C'est faux, j'ai fait un test avec ShowMessage(inttostr(INFINITE)); et ça m'a affiché 4 294 967 296, soit 49 jours si j'ai bien converti
    Ton raisonnement est faux !

    INFINITE = pas de timeout
    Timeout max = 2 147 483 647 ms

    On passe à la fonction un cardinal mais c'est traité derrière comme un entier signé et une valeur négative différente d'INFINITE est fixée à MAXINT.

    The time-out value needs to be a positive number between 0 and 0x7FFFFFFF. The maximum time-out value not equal to infinity is 0x7FFFFFFF. The infinite time-out value is 0xFFFFFFFF. Any time-out value between 0x7FFFFFFF and 0xFFFFFFFF—that is, values from 0xF0000000 through 0xFFFFFFFE—is equivalent to 0x7FFFFFFF.
    Cela dit si tu crées un thread et tout ce qui va avec pour éventuellement le démarrer tous les N jours, il y a probablement un problème de conception


    Sinon content que tu sois arrivé à tes fins

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

    Informations forums :
    Inscription : Juillet 2006
    Messages : 11 102
    Par défaut
    Bonjour,

    Citation Envoyé par Andnotor Voir le message
    Tu chipotes encore !
    Oui, et ce n'est pas fini, lire ci-après la tartine (mais comme je ne suis pas là cet aprème, ça laisse du temps pour réfléchir, )

    Citation Envoyé par Andnotor Voir le message
    Sinon content que tu sois arrivé à tes fins
    Tout ça grâce à toi et à ta patience, encore merci, et tu vas voir, ce qui suit n'est pas piqué des vers, j'ai pris des notes ce matin :

    +++++++++++++++++++++
    Avant d'aller plus loin, je fais quelques tests ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      ShowMessage(inttostr(MAXLONGINT)+' - '+inttostr(INFINITE)); // 2147483647 - 4294967295
      if WaitRunning.ManualReset then ShowMessage('WaitRunning.ManualReset is True') else ShowMessage('WaitRunning.ManualReset is False');
      if WaitFinish.ManualReset then ShowMessage('WaitFinish.ManualReset is True') else ShowMessage('WaitFinish.ManualReset is False');
      Application.Terminate; // dessus affiche WaitRunning.ManualReset is True puis WaitFinish.ManualReset is False
      ...
    end;
    La première ligne montre qu'en Lazarus la valeur d'INFINITE n'est pas la même qu'en Delphi : ça ne va pas compliquer les choses, ça ?
    D'ailleurs, je me suis ensuite rendu compte de ça, dans TThreadB.Execute :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        for i := 1 to 5 do
        begin 
          //if Terminated or (WaitRunning_MRTrue.WaitFor(INFINITE) = wrTimeout) then Break;
          // attend l'expiration du TimeOut - si c'est infini alors c'est Terminated qui fait sortir ?
          // essayé avec une valeur de 3000, empêche le Stop donc INFINITE fait tourner sans fin
          // donc on pourrait s'en passer : je le commente dessus et je mets ça dessous
          if Terminated then Break;
    et ça fonctionne aussi bien. Mais je ne sais pas si je vais le garder, j'attends vos avis.

    Ensuite les events sont définis l'un à True et l'autre à False, dans Initialization, ce qui m'a incité à jouer de la touche F1, et j'ai trouvé ça, dans TEventObject.SetEvent :
    SetEvent sets the event.
    If the ManualReset is True any thread that was waiting for the event to be set (using WaitFor) will resume it's operation. After the event was set, any thread that executes WaitFor will return at once. If ManualReset is False, only one thread will be notified that the event was set, and the event will be immediately reset after that.

    que je trouve un peu lourdingue, pour True.
    Me demande si ça ne serait pas mieux ainsi :
    If ManualReset is True any thread that was waiting for the event to be set (using WaitFor) will resume it's operation when the event will be set and will return.
    c'est plus "light", ça me plait bien !

    Et du coup j'ai renommé ces deux events, l'un comme WaitRunning_MRTrue et l'autre comme WaitFinish_MRFalse. Plus qu'à bien se souvenir, en lisant le code, de If MR True any thread will resume on event et de If MR False 1 thread notified and event reset.

    Mais il n'y a pas un mot sur le reset de l'event en cas de MR à True alors que c'est bien écrit en cas de MR à False : so what ?

    Enfin, je suis passé à 3 boutons (puisque le Stop se comporte comme un Pause), ce qui donne ça, après rajout d'une information complémentaire :
    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.btnStartClick(Sender: TObject);
    begin
      WaitRunning_MRTrue.SetEvent;
      PostMessage(Wnd, LM_LOG, AB_Texte, 1);
    end;
     
    procedure TForm1.btnPauseClick(Sender: TObject);
    begin
      WaitRunning_MRTrue.ResetEvent;
      PostMessage(Wnd, LM_LOG, AB_Texte, 2);
    end;
     
    procedure TForm1.btnStopClick(Sender: TObject);
    begin
      WaitRunning_MRTrue.ResetEvent;
      PostMessage(Wnd, LM_LOG, AB_Texte, 3);
      Counter := 0;
      PostMessage(Wnd, LM_LOG, A_Counter, Counter);
    end;
    mais ce Counter dans btnStop oblige à le déplacer du TThreadB vers
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    var
      Form1: TForm1;
      Counter :integer;
    et avec une petite modif dans LMLog :
    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
    var
      s: string;
    begin
      case Message.WParam of
        ...
        B_WakeA     : Memo1.Lines.Add('B: Wake A');
        AB_Texte    : begin
          case Message.LParam of
            1: s := 'Started';
            2: s := 'Paused';
            3: s := 'Stopped';
          end;
          Memo1.Lines.Add('Texte : ' + s);
        end;
      end;
    et l'ajout d'une constante,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    const
      ...
      AB_Texte    = 6;
    j'arrive à ça (les lignes commençant par __ ont été ajoutées après le run) :
    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
    __Start cliqué
    Texte : Started
    A: wait for B ...
    B: Working ... ... ... ... ... ...
    B: Wake A
    B: Working ...
    A: ThreadB.Counter=1
     
    A: wait for B ... ... ... ... ... ...
    B: Wake A
    B: Working ...
    A: ThreadB.Counter=2
     
    A: wait for B ... ... ... ... ... __Pause cliqué
    Texte : Paused ...
    B: Wake A
    A: ThreadB.Counter=3
     
    __pause effective ici, puis Start cliqué
    Texte : Started
    B: Working ...
    A: wait for B ... ... ... ... ... ...
    B: Wake A
    B: Working ...
    A: ThreadB.Counter=4
     
    A: wait for B ... ... ... ... ... ...
    B: Wake A
    B: Working ...
    A: ThreadB.Counter=5
     
    A: wait for B ... ...             __Stop cliqué
    Texte : Stopped
    A: ThreadB.Counter=0
     ... ... ... ...
    B: Wake A
    A: ThreadB.Counter=1
     
    __et rend la main ici
    qui me donne envie de creuser du côté de SendMessage plutôt que PostMessage car on voit bien ci-dessus que les réactions aux clics sont un peu dans le désordre : regardez les deux premières lignes avec "A: wait for B" et leurs points de suspension. Mais n'y aura-t-il pas des effets de bord ?

    Jusque là c'est très bien, sans plus aucune erreur depuis hier.

    Bon app', bon aprème, see you later, alligator !

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 921
    Par défaut
    Citation Envoyé par Jipété Voir le message
    La première ligne montre qu'en Lazarus la valeur d'INFINITE n'est pas la même qu'en Delphi : ça ne va pas compliquer les choses, ça ?
    Si c'est la même : INFINITE = DWORD($FFFFFFFF).

    Citation Envoyé par Jipété Voir le message
    et ça fonctionne aussi bien. Mais je ne sais pas si je vais le garder, j'attends vos avis.
    Tu n'as pas compris le but du WaitFor dans ce cas-là.

    WaitFor (qui n'est pas un timeout) est une méthode encapsulant une procédure (!) permettant de contrôler l'état (ou le changement d'état si timeout <> 0) d'un objet synchronisable (event, thread, process, etc.).

    Son but ici est uniquement de contrôler si le bouton Stop a été pressé afin de sortir immédiatement de la boucle (attention : Timout=0, pas INFINITE !).

    Si tu laisses seulement Terminated, oui la terminaison du thread sera plus rapide (max. ce sleep de 300 ms) mais à l'appui sur le bouton Stop (pas de sortie, Terminated=FALSE) il faudra jusqu'à 5 x 300 ms pour qu'il se mette en pause (jusqu'au retour au WaitFor(INFINITE)).

    En résumé :

    if Terminated permet de stopper plus rapidement le thread à sa terminaison ;
    if WaitRunning.WaitFor(0) = wrTimeout permet de le stopper plus rapidement à l'appui sur Stop.


    Tu n'es bien sûr pas obligé de le mettre si tu acceptes qu'à l'appui sur Stop l'arrêt ne soit pas instantané (enfin max. 300 ms).

    Citation Envoyé par Jipété Voir le message
    c'est plus "light", ça me plait bien !
    C'est plus light mais incorrect et incomplet. Mais j'admets que le texte anglais ne me semble pas top non plus (même si je ne suis pas un anglophone confirmé). Je l'exprimerais plutôt ainsi :

    If ManualReset is True any thread that was waiting for the event to be set using WaitFor will resume it's operation and any subsequent WaitFor will return at once. If ManualReset is False, only one thread will be notified that the event was set as the event is immediately reset by WaitFor.
    Citation Envoyé par Jipété Voir le message
    Mais il n'y a pas un mot sur le reset de l'event en cas de MR à True alors que c'est bien écrit en cas de MR à False : so what ?
    So what what? C'est l'explication du paramètre ManualReset. Lis l'aide sur SetEvent/ResetEvent

    Citation Envoyé par Jipété Voir le message
    Enfin, je suis passé à 3 boutons (puisque le Stop se comporte comme un Pause)
    Pour moi ce serait plutôt la destruction pure et simple de ThreadA.

    Citation Envoyé par Jipété Voir le message
    mais ce Counter dans btnStop oblige à le déplacer du TThreadB vers
    Aïe aïe aïe !

    1. Ton compteur ne sera quasiment jamais à zéro à moins d'être déjà en pause. Il faudrait vraiment un gros coup de bol pour que le pointeur de programme soit juste après Counter := Counter +1 quand WaitRunning est reseté.
    2. On accède jamais à une donnée commune depuis plusieurs threads sans un verrouillage.


    Pense multi-tâches sacrebleu !

    Citation Envoyé par Jipété Voir le message
    qui me donne envie de creuser du côté de SendMessage plutôt que PostMessage
    Tu vas juste une nouvelle fois resynchroniser avec la tâche principale comme c'était le cas au départ avec tes logs par section critique.

    La pile de message est du FIFO, aucun soucis de ce côté-là.

    Citation Envoyé par Jipété Voir le message
    Mais n'y aura-t-il pas des effets de bord ?
    Aucun ! A est synchronisé sur B par WaitFinish mais B n'attend pas A pour continuer son travail. Si tu veux quelque chose de parfaitement séquentiel, ben faut pas faire de multi-tâches

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

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

    Non, je n'ai pas oublié cette discussion, juste qu'après avoir tout bien lu et relu, j'en arrive à la conclusion que le multi-threading n'est pas un truc pour moi, trop compliqué.
    Ça me gêne un peu de le reconnaitre, je me console en me disant que tous les efforts d'Andnotor (encore merci à toi, camarade, ) seront surement profitables à certains mais moi, quand je vois qu'en cliquant sur "Stop" ça ne s'arrête pas instantanément ou presque,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    A: wait for B ... ...             __Stop cliqué
    Texte : Stopped
    A: ThreadB.Counter=0
     ... ... ... ...
    B: Wake A
    A: ThreadB.Counter=1
     
    __et rend la main ici
    je me dis que j'ai loupé une étape et que je vais dans le mur en continuant.

    On ne va pas revenir dessus, je vais repartir en mode single-threading et ça ira très bien.


    PS : j'ai déjà dit qu'il faudrait un bouton "Abandonné" à côté de "Résolu", bah, un jour peut-être...

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 921
    Par défaut
    C'est dommage de ne pas s'y mettre parce que c'est quand même un concept important mais c'est sûr que le multi-threads demande beaucoup de rigueur, surtout en matière de partage de données.

    Sinon oui on peut faire quelque chose de plus réactif mais pour ça il faut éviter les attentes non contrôlées, on évitera donc ce Sleep(300) et on tirera plutôt parti de WaitFor avec timeout. On aurait juste besoin d'un event supplémentaire dédié au stop (WaitStopped) et c'est son état qui nous dirait quoi faire.

    On utilise toujours Sleep s'il y a une boucle incontrôlée (qui tourne plein pot) mais habituellement uniquement Sleep(1) qui suffit à laisser souffler le système (à l'OS de lancer les autres tâches en attente) mais sans perdre de temps s'il a peu à faire.

    Je te mets encore un p'tit code modifié en ce sens :
    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
    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;
      WaitRunning : TEvent;
      WaitStopped : TEvent;
      WaitFinish  : TEvent;
     
    {$R *.dfm}
     
    procedure TForm6.bStartClick(Sender: TObject);
    begin
      WaitRunning.SetEvent;
    end;
     
    procedure TForm6.bStopClick(Sender: TObject);
    begin
      WaitRunning.ResetEvent;
      WaitStopped.SetEvent;
    end;
     
    procedure TForm6.FormCreate(Sender: TObject);
    begin
      Wnd     := Handle;
      ThreadA := TThreadA.Create(FALSE);
    end;
     
    procedure TForm6.FormDestroy(Sender: TObject);
    begin
      WaitRunning.SetEvent;
      WaitStopped.SetEvent;
     
      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.AddStrings([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
          WaitRunning.WaitFor;
     
          // 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
        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
          case WaitStopped.WaitFor(300) of
            wrSignaled : // Stop pressé, on quitte immédiatement cette boucle
                         Break;
     
            wrTimeout  : // Pas eu de stop après 300 ms, au boulot !
                         begin
                           PostMessage(Wnd, LM_LOG, B_Running, 0);
     
                           // Signale la fin
                           if i = 5 then
                           begin
                             Counter := Counter + 1;
                             PostMessage(Wnd, LM_LOG, B_WakeA, 1);
                             WaitFinish.SetEvent;
                           end;
                         end;
          end;
        end;
      end;
     
      WaitFinish.SetEvent;
    end;
     
    initialization
      WaitRunning := TEvent.Create(nil, TRUE, FALSE, '');
      WaitStopped := TEvent.Create(nil, FALSE, FALSE, '');
      WaitFinish  := TEvent.Create(nil, FALSE, FALSE, '');
     
    finalization
      WaitRunning.Free;
      WaitStopped.Free;
      WaitFinish.Free;

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