6
votes

Comment un fil peut-il notifier un objet qui n'a pas de poignée de fenêtre?

Je suis nouveau à multithreading, mais pas un novice complet. J'ai besoin d'effectuer un appel à un service Web dans un thread de travailleur.

dans le fil principal, j'ai une forme (Tform) avec un membre de données privé (chaîne privée) que seul le fil du travailleur écrira (je passe le pointeur sur le fil avant qu'il ne reprenne). Lorsque le fil du travailleur a terminé son appel de service Web et écrit la réponse résultante XML au membre privé sur le formulaire, le thread du travailleur utilise PostMessage pour envoyer un message à la poignée du formulaire (que j'ai également transmis dans le fil avant de reprendre). < / p> xxx

Cela fonctionne bien, mais maintenant je veux faire la même chose à partir d'une source de données (qui n'a pas de poignée) ... J'apprécierais vraiment un code utile pour compléter le modèle de travail que j'ai.

edit

Ce que je veux vraiment, c'est le code (si possible) Permettez-moi de remplacer la ligne xxx

avec xxx


0 commentaires

3 Réponses :


9
votes

J'ai utilisé cette technique avant de réussir: Envoyer des messages à des messages non Applications fenêtres

Fondamentalement, utilisez un deuxième thread en tant que pompe à message sur une poignée obtenue via AllateHwnd. Ceci est certes irritant et vous feriez mieux d'utiliser une bibliothèque pour gérer tous les détails. Je préfère omnithreadlibrary mais il y en a d'autres - voir Comment choisir entre les différentes façons de faire de la filetage dans Delphi? et < Un href = "https://stackoverflow.com/questions/670641/delphi-threading-frameworks"> Delphi - Frameformes de filetage .


2 commentaires

+1 pour ne pas réinventer la roue. Regarde omnithreadlibrary.


Allocathwnd a fait le travail. Merci Moz.



5
votes

Vous pouvez allouer votre propre poignée avec AllocHWND et utiliser cela comme une cible postmessage.

TTestThread = class(TThread)
private
  FSignalShutdown: boolean;
  // hidden window handle 
  FWinHandle: HWND;                       
protected
  procedure Execute; override;
  // our window procedure 
  procedure WndProc(var msg: TMessage);   
public
  constructor Create;
  destructor Destroy; override;
  procedure PrintMsg;
end;

constructor TTestThread.Create;
begin
  FSignalShutdown := False;

  // create the hidden window, store it's 
  // handle and change the default window 
  // procedure provided by Windows with our 
  // window procedure 

  FWinHandle := AllocateHWND(WndProc);
  inherited Create(False);
end;

destructor TTestThread.Destroy;
begin
  // destroy the hidden window and free up memory 
  DeallocateHWnd(FWinHandle);
  inherited;
end;

procedure TTestThread.WndProc(var msg: TMessage);
begin
  if Msg.Msg = WM_SHUTDOWN_THREADS then
    // if the message id is WM_SHUTDOWN_THREADS
    // do our own processing        
    FSignalShutdown := True 
  else        
    // for all other messages call 
    // the default window procedure 
    Msg.Result := DefWindowProc(FWinHandle, Msg.Msg, 
                                Msg.wParam, Msg.lParam);
end;


3 commentaires

Que Allocathwnd n'est pas un fil de sécurité est une source de beaucoup d'irritation. Il semble que Embarcadero ne change pas leur politique. J'utilise mon propre code d'accrochage noddy pour remplacer Allochwnd au moment de l'exécution avec une version protégée par une section critique.


@David: D'accord, mais il y a des choses dans leur code qui sont beaucoup plus irritantes et qui ne sont toujours pas réparées :) Je serais heureux si cela ne serait que Allocathwnd.


Tant que le nouveau thread est créé par le fil de l'interface graphique, il n'y a aucun problème avec ce code. Le constructeur fonctionne dans le contexte du fil qui l'appelle.



3
votes

Alternatives basées sur l'utilisation d'un événement:

  1. Utilisez à l'onduleur du fil (déjà présent ) En combinaison avec un drapeau: p>

    TMyDataModule = class(TDataModule)
    private    
      procedure OnWebServiceCallComplete(Sender: TObject);
    ...  
    
    TMyThread = class(TThread)
    public
      property TerminateFlag: Integer ...
    ...
    
    procedure TMyDataModule.StartWorkerThread;
      ...
      MyWorkerThread.OnTerminate := <Self.>OnWebServiceCallComplete;
      ...
    
    procedure TMyDataModule.OnWebServiceCallComplete(Sender: TObject);
    begin
      if MyWorkerThread.TerminateFlag = WEBCALL_COMPLETE then
        ...
    end;
    
  2. Ajoutez une nouvelle propriété d'événement à la classe de thread dans laquelle vous pouvez fournir le drapeau sous forme de paramètre pour indiquer le résultat de terminaison / thread. Quelque chose comme montré ici . Assurez-vous de synchroniser l'appel de l'événement. Ou oubliez le paramètre et appelez simplement l'événement si l'exécution est terminée de manière gracieuse (comme si vous le faisez maintenant). P> LI> ol> p>


2 commentaires

J'apprécie vraiment cette alternative. Il semble plus élégant. Je vais l'essayer.


À la deuxième pensée, l'utilisation de messages est mon modèle préféré. Merci quand même.