Accès à la variable dans le formulaire parent à partir d'un événement OnTimer - Obtenir une exception

StackOverflow https://stackoverflow.com/questions/499737

Question

Je reçois une exception dans un gestionnaire d'événement OnTimer (TTimer) qui, une fois exécuté, incrémente une variable entière dans la fiche parent. Les minuteries doivent pouvoir accéder à un entier incrémenté utilisé comme identifiant.

Ma première question est la suivante: comment savoir dans Delphi 2007 quel code est exécuté dans quel thread? Existe-t-il un moyen en mode débogage pour inspecter cela afin que je puisse déterminer avec certitude?

Deuxièmement, si je dois accéder à des variables d’un formulaire parent à partir d’un autre thread et les modifier, quel est le meilleur moyen de le faire? Il semble que parfois Delphi me permet d'accéder à ces variables & "Incorrectement &"; sans exception et d'autres fois, cela donne une exception.

Était-ce utile?

La solution

Juste pour être sûr: vous parlez d'un événement de minuterie, de l'autre du multithreading. Ce sont deux façons totalement différentes d’exécuter du code en parallèle.

Une minuterie sera toujours exécutée dans le thread principal. Il devrait être sécuritaire d’accéder à tout ce qui a été créé et utilisé dans le fil principal. En fait, un événement de minuterie ne peut se produire que si aucun autre code de thread principal n'est en cours d'exécution, car il a besoin du gestionnaire de messages de l'application pour traiter le message de minuterie. C'est donc soit en dehors de tout code de gestion d'événement, soit lorsqu'un de vos gestionnaires d'événement appelle Application.ProcessMessages.

Un fil est très différent de cela. Dans ce cas, le code dans différents threads s'exécute indépendamment les uns des autres. Si vous utilisez une machine multiprocesseur (ou multi-core), il est même possible qu’elle fonctionne réellement en parallèle. Vous pouvez avoir de nombreux problèmes de cette façon, en particulier la VCL de Delphi (qui inclut Delphi XE) n’est pas une sauvegarde de thread. Par conséquent, les appels à une classe VCL ne doivent être effectués qu’à partir du thread principal (il existe quelques exceptions à cette règle).

Alors, veuillez tout d’abord préciser si vous parlez de minuteries ou de multithreading, avant d’attendre des réponses utiles.

Autres conseils

  

Comment puis-je savoir dans Delphi 2007 lequel   code est en cours d'exécution dans quel fil? Est   il y a un moyen en mode débogage d'inspecter   cela afin que je puisse déterminer à coup sûr?

Vous pouvez définir un point d'arrêt. Lorsque l'exécution s'arrête, consultez la fenêtre de débogage des threads. Double-cliquez sur chaque thread pour afficher sa pile d'appels dans la fenêtre de débogage de la pile. Vous pouvez également utiliser la fonction Win32 GetCurrentThreadId pour connaître le thread actuel (par exemple, pour la journalisation ou pour déterminer si le thread actuel est le thread principal, etc.).

Puisque vous n’affichez aucun code, il est difficile d’être plus précis. Juste pour être sûr: le code dans un gestionnaire d'événements timer n'est pas exécuté dans un autre thread. Si vous utilisez uniquement des minuteries, vous ne rencontrerez pas de problèmes d'accès simultané, et non de véritables threads d'arrière-plan.

  

Deuxièmement, si je dois accéder et   modifier les variables sous une forme parent de   un autre fil, quel est le meilleur moyen   pour faire ça? Il semble que parfois   Delphi me permet d'accéder à ces   variables " incorrectement " sans donner   une exception et d'autres fois il fait   donner une exception.

Si vous êtes vraiment dans un autre thread et accédez à une variable partagée, vous pouvez voir toutes sortes de choses qui se passent si vous ne protégez pas cet accès. La plupart du temps, cela peut fonctionner correctement ou vous obtenez des valeurs étranges. Si vous souhaitez simplement modifier un entier de manière thread-safe, consultez InterlockedIncrement. Sinon, vous pourriez utiliser une section critique, mutex, monitor ... JEDI possède quelques classes utiles dans l'unité JclSynch pour cela.

Vous posez deux questions, je vais donc y répondre en deux réponses.

Votre première question concerne l'utilisation de TTimers. ceux qui fonctionnent toujours dans le fil principal.

Très probablement, votre exception est une violation d'accès.

Si tel est le cas, cela est généralement causé par l'un de ces problèmes:

  • a- votre formulaire parent est déjà détruit lorsque votre TTimer tire.
  • b- vous n'avez pas encore de référence à votre formulaire parent lorsque votre TTimer incendies.

b est facile: vérifiez si votre référence est nil .

a est plus difficile et dépend de la façon dont vous référencez votre formulaire parent.

En gros, vous voulez vous assurer que votre référence devient nulle lorsque le parent est détruit ou supprimé.

Si vous faites référence à votre formulaire parent via une variable globale (dans cet exemple, via Form2 ), TForm2 doit créer la variable Form2 nil à l'aide de l'événement OnDestroy, comme ceci:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm2 = class(TForm)
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.FormDestroy(Sender: TObject);
begin
  Form2 := nil;
end;

end.

Si vous utilisez une référence de champ à votre formulaire parent (comme FMyForm2Reference ), vous devez utiliser l'option Add une méthode de notification comme celle-ci:

unit Unit1;

interface

 uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Unit2;

 type
  TForm1 = class(TForm)
  private
    FMyForm2Reference: TForm2;
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  public
  end;

 var
  Form1: TForm1;

 implementation

{$R *.dfm}

 procedure TForm1.Notification(AComponent: TComponent; Operation: TOperation);
 begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) then
    if (AComponent = FMyForm2Reference) then
      FMyForm2Reference := nil;
 end;

 end.

Cordialement,

Jeroen Pluimers

Vous posez deux questions, je vais donc y répondre en deux réponses.

Votre deuxième question concerne l’assurance qu’un seul thread accède à une variable dans un formulaire à la fois.

La variable étant dans un formulaire, le meilleur moyen consiste à utiliser la méthode Synchroniser pour cela.

Il existe un excellent exemple de cela livré avec Delphi, c’est dans le projet thrddemo.dpr , où l'unité dans SortThds.pas utilise cette méthode qui montre comment l'utiliser:

procedure TSortThread.VisualSwap(A, B, I, J: Integer);
 begin
  FA := A;
  FB := B;
  FI := I;
  FJ := J;
  Synchronize(DoVisualSwap);
 end;

Bonne chance,

Jeroen Pluimers

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top