Delphi (XE2) Indy (10) Multithread Ping
J'ai une chambre avec 60 ordinateurs/périphériques (40 ordinateurs et 20 oscilloscopes Windows CE) et je voudrais savoir qui et tout le monde est en vie à l'aide de la commande ping. J'ai d'abord écrit un ping standard (voir ici Delphi Indy Ping Erreur 10040), qui fonctionne bien maintenant, mais prend une éternité quand la plupart des ordinateurs sont en mode hors connexion.
Donc ce que je suis en train de faire est d'écrire un MultiThread Ping mais je suis tout à fait de la difficulté avec elle. Je n'ai vu que très peu d'exemples sur internet et personne n'a été correspondre à mes besoins, c'est pourquoi j'ai essayer de l'écrire moi-même.
- Je utiliser XE2 et Indy 10 et le formulaire est uniquement constitué d'un mémo et un bouton.
unit Main;
interface
uses
Winapi.Windows, System.SysUtils, System.Classes, Vcl.Forms,
IdIcmpClient, IdGlobal, Vcl.StdCtrls, Vcl.Controls;
type
TMainForm = class(TForm)
Memo1: TMemo;
ButtonStartPing: TButton;
procedure ButtonStartPingClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TMyPingThread = class(TThread)
private
fIndex : integer;
fIdIcmpClient: TIdIcmpClient;
procedure doOnPingReply;
protected
procedure Execute; override;
public
constructor Create(index: integer);
end;
var
MainForm: TMainForm;
ThreadCOunt : integer;
implementation
{$R *.dfm}
constructor TMyPingThread.Create(index: integer);
begin
inherited Create(false);
fIndex := index;
fIdIcmpClient := TIdIcmpClient.Create(nil);
fIdIcmpClient.ReceiveTimeout := 200;
fIdIcmpClient.PacketSize := 24;
fIdIcmpClient.Protocol := 1;
fIdIcmpClient.IPVersion := Id_IPv4;
//first computer is at adresse 211
fIdIcmpClient.Host := '128.178.26.'+inttostr(211+index-1);
self.FreeOnTerminate := true;
end;
procedure TMyPingThread.doOnPingReply;
begin
MainForm.Memo1.lines.add(inttostr(findex)+' '+fIdIcmpClient.ReplyStatus.Msg);
dec(ThreadCount);
if ThreadCount = 0 then
MainForm.Memo1.lines.add('--- End ---');
end;
procedure TMyPingThread.Execute;
begin
inherited;
try
fIdIcmpClient.Ping('',findex);
except
end;
while not Terminated do
begin
if fIdIcmpClient.ReplyStatus.SequenceId = findex then Terminate;
end;
Synchronize(doOnPingReply);
fIdIcmpClient.Free;
end;
procedure TMainForm.ButtonStartPingClick(Sender: TObject);
var
i: integer;
myPing : TMyPingThread;
begin
Memo1.Lines.Clear;
ThreadCount := 0;
for i := 1 to 40 do
begin
inc(ThreadCount);
myPing := TMyPingThread.Create(i);
//sleep(10);
end;
end;
end.
Mon problème est qu'il "semble" fonctionner lorsque je décommentez la "sleep(10)", et "semble" ne pas travailler sans elle. Cela pour vous dire que je suis manque un point dans le thread que j'ai écrit.
En d'autres termes. Quand le Sommeil(10) est dans le code. Chaque fois que j'ai cliqué sur le bouton pour obtenir de vérifier les connexions, le résultat était correct.
Sans sommeil(10), c'est le travail "en plus" de temps en temps, mais, à certains moments, le résultat est mauvais, me donnant un écho ping sur les ordinateurs hors-ligne et aucun écho ping sur ordinateur en ligne, est la réponse ping n'a pas été attribué pour le bon thread.
Tout commentaire ou aide est la bienvenue.
----- EDIT /IMPORTANT -----
Général suivi de cette question, @Darian Miller a commencé un Google Code du projet ici https://code.google.com/p/delphi-stackoverflow/ qui est une base de travail. Je marque sa réponse comme "accepté de répondre" mais les utilisateurs doivent se référer à ce projet open source (tout le crédit appartient à lui) car il va sûrement être étendu et mis à jour dans le futur.
source d'informationauteur HpTerm
Vous devez vous connecter pour publier un commentaire.
Remy a expliqué les problèmes... j'ai voulu le faire en Indy pour un moment donc j'ai posté une solution possible que je viens de mettre ensemble pour un nouveau Google Code du projet au lieu d'avoir un long commentaire ici. C'est un premier coup de couteau genre de chose, laissez-moi savoir si vous avez quelques changements à intégrer:
https://code.google.com/p/delphi-vault/
Ce code a deux façons de Ping...multi-thread clients, comme dans votre exemple, ou avec une simple procédure de rappel. Écrit pour Indy10 et, plus tard, des versions de Delphi.
Votre code permettrait de mettre fin à l'aide d'un TThreadedPing descendant de la définition d'un SynchronizedResponse méthode:
Et déclenche des threads de client, le code devient quelque chose comme:
Le filetage de réponse est appelée dans la méthode synchronisée:
La racine du problème est que les pings sont sans connexion de la circulation. Si vous avez plusieurs
TIdIcmpClient
objets d'interroger le réseau en même temps, unTIdIcmpClient
instance peut recevoir une réponse qui appartient en réalité à un autreTIdIcmpClient
instance. Vous essayez de tenir compte de cela dans votre boucle de fil, en vérifiantSequenceId
valeurs, mais vous ne prenez pas en compte le fait queTIdIcmpClient
déjà fait la même vérifier en interne. Il lit réseau réponses dans une boucle jusqu'à ce qu'il reçoit la réponse qu'il attend, ou jusqu'à ce que leReceiveTimeout
se produit. S'il reçoit une réponse, il n'attend pas, il suffit simplement de se défausse de cette réponse. Ainsi, si l'onTIdIcmpClient
instance rejette une réponse d'un autreTIdIcmpClient
instance attendais, cette réponse ne va pas être traités par votre code, et que d'autresTIdIcmpClient
serez susceptibles de recevoir une autreTIdIcmpClient
's réponse, et ainsi de suite. En ajoutant leSleep()
vous êtes à la baisse (mais pas éliminer) les chances que les pings se chevauchent les uns les autres.Pour ce que vous tentez de le faire, vous ne serez pas en mesure d'utiliser
TIdIcmpClient
comme-est d'avoir plusieurs pings fonctionnent en parallèle, désolé. Il n'est tout simplement pas conçu pour cela. Il n'y a aucun moyen de différencier les données de réponse de la façon dont vous avez besoin. Vous aurez pour sérialiser vos fils, un seul thread peut appelerTIdIcmpClient.Ping()
à la fois.Si la sérialisation de l'pings n'est pas une option pour vous, vous pouvez essayer de copier des parties de
TIdIcmpClient
code source dans votre propre code. 41 les threads en cours d'exécution - dispositif 40 fils et 1 réponse fil. Créer un socket unique que tous les threads partagent. Demandez à chaque appareil fil de préparer et d'envoyer ses requêtes ping au réseau à l'aide de cette prise. Alors la réponse de fil en continu la lecture des réponses à partir de la même prise de routage et à la appropriée de l'appareil de thread pour le traitement. C'est un peu plus de travail, mais il vous donnera les multiples ping parallélisme vous êtes à la recherche pour.Si vous ne voulez pas aller à tout ce mal, une alternative est d'utiliser une application tierce qui prend déjà en charge le ping plusieurs machines en même temps, comme FREEPing.
Je n'ai pas essayé ton code, donc c'est tout hypothétique, mais je pense que vous foiré le filetage et le got classique
race condition
. Je rappelle mes conseils pour utiliserAsyncCalls
ouOmniThreadLibrary
- ils sont beaucoup plus simple et qui serait vous faire économiser quelques tentatives de "prise de vue de votre propre pied".Threads sont faits pour minimiser la main-fil de charge. Thread constructeur doit faire un minimum de travail de se souvenir de paramètres. Personnellement je suis passé idICMP création en
.Execute
méthode. Si, pour une raison quelconque, il faudra créer son interne des objets de synchronisation, comme la fenêtre et de la file d'attente de message ou signal ou que ce soit, j'aimerais qu'il arrive déjà dans un nouveau engendré fil.Il n'y a pas de sens "héritée"";".Exécuter. Mieux le supprimer.
Faire taire toutes les exceptions est mauvais style. Vous avez probablement des erreurs - mais n'ont aucun moyen de savoir à leur sujet. Vous devez propager vers thread principal et de les afficher. OTL et CA vous aider dans ce, alors que pour tThread vous devez le faire manuellement. Comment Gérer les Exceptions levées dans AsyncCalls fonction sans appel .Sync?
Logique d'Exception est erronée. Il est inutile d'avoir une boucle si exception - si pas de succès de Ping a été mis alors pourquoi en attente de réponse ? Vous boucle doit aller à l'intérieur même essayer-à l'exception de cadre à l'émission de ping.
Votre
doOnPingReply
s'exécute APRÈSfIdIcmpClient.Free
encore accèdefIdIcmpClient
's internes. Essayé de changer .Gratuit pour FreeAndNil ?C'est une erreur classique de l'utilisation de morts pointeur après la libération.
La bonne méthode serait de:
5.1. qu'il soit libre de l'objet dans
doOnPingReply
5.2. ou de copier toutes les données pertinentes de
doOnPingReply
de TThread privée du membre vars avant d'appeler les deuxSynchronize
etidICMP.Free
(et de n'utiliser ces revendeurs à valeur ajoutée dansdoOnPingReply
)5.3. seulement
fIdIcmpClient.Free
à l'intérieur deTMyThread.BeforeDestruction
ouTMyThread.Destroy
. Après tout, si vous avez choisi de créer l'objet dans le constructeur - alors vous devriez gratuit en correspondant à la langue de construire - destructeur.Puisque vous ne gardez pas les références au fil des objets - qui
While not Terminated
boucle semble redondant. Juste faire de l'habitude, jamais de la boucle et de les appeler pause.Ladite boucle est CPU-faim, c'est comme tourner en boucle. Veuillez appeler
Sleep(0);
ouYield();
à l'intérieur de la boucle de donner à d'autres threads plus de chance de mieux faire leur travail. Ne fonctionne pas agaisnt OS planificateur ici - vous ne sont pas dans un speed-chemin critique, aucune raison de fairespinlock
ici.Dans l'ensemble, je considère que:
Drôle, c'est l'exemple du bug qui
FreeAndNil
pourrait exposer et de mettre en évidence, tandis que FreeAndNil-haters sont en affirmant qu'il "cache" bugs.