Accès à la variable dans le formulaire parent à partir d'un événement OnTimer - Obtenir une exception
-
20-08-2019 - |
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.
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