7

ネットワーク内の多くのデバイス (300 以上) を繰り返し ping でポーリングします。

プログラムはデバイスを順番にポーリングするため、低速です。投票速度を上げたい。

Delphi 7 でこれを行う方法がいくつかあります。

  1. 各デバイスには、ping を実行するスレッドがあります。スレッドを手動で管理します。
  2. Indy 10 を学び、使用します。例が必要です。
  3. ウィンドウ メッセージに基づいてオーバーラップ I/O を使用します。
  4. イベントに基づいて完了ポートを使用します。

より速く、より簡単なことは何ですか?たとえば、いくつかの例やリンクを提供してください。

4

6 に答える 6

9

ネットワークを ICMP でフラッディングするのは得策ではありません。

ある種のスレッド プールを検討し、ping 要求をキューに入れ、要求を実行するスレッドの数を固定したい場合があります。

于 2011-02-03T14:20:04.117 に答える
6

個人的には IOCP を使用します。私はそれを NexusDB のトランスポート実装にうまく使用しています。

ブロッキング ソケットとスレッドを並行して使用して 300 の送受信サイクルを実行する場合、300 のスレッドが必要になります。

IOCP を使用すると、ソケットを IOCP に関連付けた後、300 の送信操作を実行できます。送信操作は、操作が完了する前に即座に返されます。操作が完了すると、いわゆる完了パッケージが IOCP のキューに入れられます。その後、IOCP で待機しているスレッドのプールがあり、OS は完了パケットが着信するとスレッドを起動します。完了した送信操作に反応して、受信操作を実行できます。受信操作も即座に戻り、実際に完了すると IOCP のキューに入れられます。

IOCP の本当の特別な点は、どのスレッドがそれに属しているかを認識し、現在完了パッケージを処理していることです。また、IOCP は、(カーネル モードの待機状態にない) アクティブなスレッドの総数が IOCP の同時実行数 (既定では、マシンで使用可能な論理コアの数に等しい) よりも少ない場合にのみ、新しいスレッドをウェイクアップします。また、IOCP で完了パッケージを待機しているスレッド (アクティブなスレッドの数が同時実行数と等しいため、完了パッケージがキューに入れられているにもかかわらず、まだ開始されていないスレッド) がある場合、現在処理中のスレッドの 1 つ何らかの理由で完了パッケージがカーネル モードの待機状態になると、待機中のスレッドの 1 つが開始されます。

IOCP に戻るスレッドは、完了パッケージを LIFO 順にピックアップします。つまり、スレッドが IOCP に戻り、まだ待機中の完了パッケージがある場合、そのスレッドは、待機状態になり、最も長い時間待機しているスレッドがウェイクアップするのではなく、次の完了パッケージを直接取得します。

最適な条件下では、同時に実行される使用可能なコアの数 (各コアに 1 つ) に等しい数のスレッドがあり、次の完了パッケージを取得して処理し、IOCP に戻って次の完了パッケージを直接取得します。カーネル モードの待機状態やスレッド コンテキストの切り替えに入る必要はありません。

代わりに 300 のスレッドとブロック操作がある場合、少なくとも 300 MB のアドレス空間 (スタック用に予約されたスペース用) を浪費するだけでなく、1 つのスレッドが待機状態 (待機中) に入ると、一定のスレッド コンテキスト スイッチも発生します。送信または受信が完了するまで)、送信または受信が完了した次のスレッドが起動します。– Thorsten Engler 12時間前

于 2011-02-03T14:56:33.490 に答える
5

Windows では、直接 ICMP アクセスは非推奨です。Windows 上の ICMP プロトコルへの直接アクセスが制御されます。ICMP/ping/traceroute スタイルの raw ソケットの悪用により、Windows の一部のバージョンでは Windows 独自の API を使用する必要があると思います。特に、Windows XP、Vista、および Windows 7 では、ユーザー プログラムが raw ソケットにアクセスできません。

一部の Delphi ping コンポーネントが行う ICMP.dll の既定の機能を使用しましたが、以下のコメントは、これが「文書化されていない API インターフェイスを使用している」と見なされるという事実を警告しました。

メインの Delphi ping コンポーネント呼び出し自体のサンプルを次に示します。

function TICMP.ping: pIcmpEchoReply;
{var  }
begin
  // Get/Set address to ping
  if ResolveAddress = True then begin
    // Send packet and block till timeout or response
    _NPkts := _IcmpSendEcho(_hICMP, _Address,
                            _pEchoRequestData, _EchoRequestSize,
                            @_IPOptions,
                            _pIPEchoReply, _EchoReplySize,
                           _TimeOut);
    if _NPkts = 0 then begin
      result := nil;
      status := CICMP_NO_RESPONSE;
    end else begin
      result := _pIPEchoReply;
    end;
  end else begin
    status := CICMP_RESOLVE_ERROR;
    result := nil;
  end;
end;

最新の Ping コンポーネントの実装のほとんどは、上記と同様のコードに基づいていると思います。私はそれを使用して、問題なくバックグラウンド スレッドでこの ping 操作を実行しました。(以下のリンクにデモ プログラムが含まれています)。

ICMP.DLL ベースのデモの完全なサンプル ソース コードはこちらにあります。

更新より最新の IPHLPAPI.DLL サンプルが About.comにあります。

于 2011-02-03T14:46:17.477 に答える
4

IOCP を使用してスレッド プールを作成する方法を示すDelphi3000の記事を次に示します。私はこのコードの作成者ではありませんが、作成者の情報はソース コードにあります。

ここにコメントとコードを再投稿しています:

誰もがスレッドとは何か、スレッドの原理などを理解しているはずです。スレッドの単純な機能は、あるスレッドから別のスレッドに処理を分離し、同時実行と並列実行を可能にすることです。スレッドの主な原則は単純で、スレッド間で参照される割り当てられたメモリは、アクセスの安全性を確保するためにマーシャリングする必要があります。他にも多くの原則がありますが、これは本当に気にするべきものです。

そして、..

スレッド セーフ キューを使用すると、複数のスレッドが First on First off ベースで安全にキューに値を追加および削除、プッシュおよびポップできます。効率的で適切に作成されたキューを使用すると、スレッド セーフ ロギングの支援からリクエストの非同期処理まで、スレッド化されたアプリケーションの開発に非常に役立つコンポーネントを使用できます。

スレッドプールは、リクエストのキューを管理するために最も一般的に使用されるスレッドまたは複数のスレッドです。たとえば、処理が必要な要求の連続キューを持つ Web サーバーは、スレッド プールを使用して http 要求を管理するか、COM+ または DCOM サーバーはスレッド プールを使用して rpc 要求を処理します。これは、1 つの要求の処理が別の要求に与える影響を少なくするために行われます。たとえば、3 つの要求を同期的に実行し、最初の要求が完了するまでに 1 分かかった場合、2 番目の 2 つの要求は少なくとも 1 分間は完了しません。ほとんどのクライアントにとって、これは受け入れられません。

それで、これを行う方法..

行列からスタート!!

Delphi は利用可能な TQueue オブジェクトを提供しますが、残念ながらスレッド セーフではなく、実際にはあまり効率的でもありませんが、Contnrs.pas ファイルを調べて、ボーランドがそこにスタックとキューを書き込む方法を確認する必要があります。キューに必要な主な機能は、追加と削除/プッシュとポップの 2 つだけです。Add/push は、値、ポインター、またはオブジェクトをキューの最後に追加します。また、remove/pop はキューの最初の値を削除して返します。

TQueue オブジェクトから派生させて、保護されたメソッドをオーバーライドし、クリティカル セクションを追加することもできます。これにより、ある程度の方法が得られますが、新しいリクエストがキューに入るまでキューを待機させ、スレッドを次の状態にする必要があります。新しいリクエストを待つ間、休息します。これは、ミューテックスまたはシグナル イベントを追加することで実行できますが、もっと簡単な方法があります。Windows API は、キューへのスレッド セーフなアクセスを提供する IO 完了キューと、キューで新しい要求を待機している間の休息状態を提供します。

スレッド プールの実装

スレッド プールは非常に単純なものになり、必要な数のスレッドを管理し、各キュー要求を処理対象のイベントに渡します。TThread クラスを実装し、ロジックを実装してクラスの実行イベント内にカプセル化する必要はほとんどないため、別のスレッドのコンテキスト内で任意のオブジェクトの任意のメソッドを実行する単純な TSimpleThread クラスを作成できます。人々がこれを理解したら、あとは割り当てられたメモリだけを気にする必要があります。

実装方法は次のとおりです。

TThreadQueue と TThreadPool の実装

(* Implemented for Delphi3000.com Articles, 11/01/2004
        Chris Baldwin
        Director & Chief Architect
        Alive Technology Limited
        http://www.alivetechnology.com
*)
unit ThreadUtilities;

uses Windows, SysUtils, Classes;

type
    EThreadStackFinalized = class(Exception);
    TSimpleThread = class;

    // Thread Safe Pointer Queue
    TThreadQueue = class
    private
        FFinalized: Boolean;
        FIOQueue: THandle;
    public
        constructor Create;
        destructor Destroy; override;
        procedure Finalize;
        procedure Push(Data: Pointer);
        function Pop(var Data: Pointer): Boolean;
        property Finalized: Boolean read FFinalized;
    end;

    TThreadExecuteEvent = procedure (Thread: TThread) of object;

    TSimpleThread = class(TThread)
    private
        FExecuteEvent: TThreadExecuteEvent;
    protected
        procedure Execute(); override;
    public
        constructor Create(CreateSuspended: Boolean; ExecuteEvent: TThreadExecuteEvent; AFreeOnTerminate: Boolean);
    end;

    TThreadPoolEvent = procedure (Data: Pointer; AThread: TThread) of Object;

    TThreadPool = class(TObject)
    private
        FThreads: TList;
        FThreadQueue: TThreadQueue;
        FHandlePoolEvent: TThreadPoolEvent;
        procedure DoHandleThreadExecute(Thread: TThread);
    public
        constructor Create( HandlePoolEvent: TThreadPoolEvent; MaxThreads: Integer = 1); virtual;
        destructor Destroy; override;
        procedure Add(const Data: Pointer);
    end;

implementation

{ TThreadQueue }

constructor TThreadQueue.Create;
begin
    //-- Create IO Completion Queue
    FIOQueue := CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
    FFinalized := False;
end;

destructor TThreadQueue.Destroy;
begin
    //-- Destroy Completion Queue
    if (FIOQueue <> 0) then
        CloseHandle(FIOQueue);
    inherited;
end;

procedure TThreadQueue.Finalize;
begin
    //-- Post a finialize pointer on to the queue
    PostQueuedCompletionStatus(FIOQueue, 0, 0, Pointer($FFFFFFFF));
    FFinalized := True;
end;

(* Pop will return false if the queue is completed *)
function TThreadQueue.Pop(var Data: Pointer): Boolean;
var
    A: Cardinal;
    OL: POverLapped;
begin
    Result := True;
    if (not FFinalized) then
//-- Remove/Pop the first pointer from the queue or wait
        GetQueuedCompletionStatus(FIOQueue, A, Cardinal(Data), OL, INFINITE);

    //-- Check if we have finalized the queue for completion
    if FFinalized or (OL = Pointer($FFFFFFFF)) then begin
        Data := nil;
        Result := False;
        Finalize;
    end;
end;

procedure TThreadQueue.Push(Data: Pointer);
begin
    if FFinalized then
        Raise EThreadStackFinalized.Create('Stack is finalized');
    //-- Add/Push a pointer on to the end of the queue
    PostQueuedCompletionStatus(FIOQueue, 0, Cardinal(Data), nil);
end;

{ TSimpleThread }

constructor TSimpleThread.Create(CreateSuspended: Boolean;
  ExecuteEvent: TThreadExecuteEvent; AFreeOnTerminate: Boolean);
begin
    FreeOnTerminate := AFreeOnTerminate;
    FExecuteEvent := ExecuteEvent;
    inherited Create(CreateSuspended);
end;

procedure TSimpleThread.Execute;
begin
    if Assigned(FExecuteEvent) then
        FExecuteEvent(Self);
end;

{ TThreadPool }

procedure TThreadPool.Add(const Data: Pointer);
begin
    FThreadQueue.Push(Data);
end;

constructor TThreadPool.Create(HandlePoolEvent: TThreadPoolEvent;
  MaxThreads: Integer);
begin
    FHandlePoolEvent := HandlePoolEvent;
    FThreadQueue := TThreadQueue.Create;
    FThreads := TList.Create;
    while FThreads.Count < MaxThreads do
        FThreads.Add(TSimpleThread.Create(False, DoHandleThreadExecute, False));
end;

destructor TThreadPool.Destroy;
var
    t: Integer;
begin
    FThreadQueue.Finalize;
    for t := 0 to FThreads.Count-1 do
        TThread(FThreads[t]).Terminate;
    while (FThreads.Count > 0) do begin
        TThread(FThreads[0]).WaitFor;
        TThread(FThreads[0]).Free;
        FThreads.Delete(0);
    end;
    FThreadQueue.Free;
    FThreads.Free;
    inherited;
end;

procedure TThreadPool.DoHandleThreadExecute(Thread: TThread);
var
    Data: Pointer;
begin
    while FThreadQueue.Pop(Data) and (not TSimpleThread(Thread).Terminated) do begin
        try
            FHandlePoolEvent(Data, Thread);
        except
        end;
    end;
end;

end. 

ご覧のとおり、非常に簡単です。これにより、スレッドを介したリクエストのキューイングを非常に簡単に実装でき、スレッド化を必要とするあらゆるタイプの要件をこれらのオブジェクトを使用して実行できるため、多くの時間と労力を節約できます。

これを使用して、1 つのスレッドから複数のスレッドへの要求をキューに入れたり、複数のスレッドから 1 つのスレッドへの要求をキューに入れたりすることができ、これは非常に優れたソリューションになります。

これらのオブジェクトの使用例をいくつか示します。

スレッド セーフ ロギング

複数のスレッドがログ ファイルに非同期的に書き込むことができるようにします。

uses Windows, ThreadUtilities,...;

type
    PLogRequest = ^TLogRequest;
    TLogRequest = record
        LogText: String;
    end;

    TThreadFileLog = class(TObject)
    private
        FFileName: String;
        FThreadPool: TThreadPool;
        procedure HandleLogRequest(Data: Pointer; AThread: TThread);
    public
        constructor Create(const FileName: string);
        destructor Destroy; override;
        procedure Log(const LogText: string);
    end;

implementation

(* Simple reuse of a logtofile function for example *)
procedure LogToFile(const FileName, LogString: String);
var
    F: TextFile;
begin
    AssignFile(F, FileName);
    if not FileExists(FileName) then
        Rewrite(F)
    else
        Append(F);
    try
        Writeln(F, DateTimeToStr(Now) + ': ' + LogString);
    finally
        CloseFile(F);
    end;
end;

constructor TThreadFileLog.Create(const FileName: string);
begin
    FFileName := FileName;
    //-- Pool of one thread to handle queue of logs
    FThreadPool := TThreadPool.Create(HandleLogRequest, 1);
end;

destructor TThreadFileLog.Destroy;
begin
    FThreadPool.Free;
    inherited;
end;

procedure TThreadFileLog.HandleLogRequest(Data: Pointer; AThread: TThread);
var
    Request: PLogRequest;
begin
    Request := Data;
    try
        LogToFile(FFileName, Request^.LogText);
    finally
        Dispose(Request);
    end;
end;

procedure TThreadFileLog.Log(const LogText: string);
var
    Request: PLogRequest;
begin
    New(Request);
    Request^.LogText := LogText;
    FThreadPool.Add(Request);
end;

これはファイルにログを記録しているため、すべてのリクエストを 1 つのスレッドに処理しますが、より多くのスレッド カウントを使用して豊富な電子メール通知を行うことができます。または、プログラムで何が起こっているか、またはプログラムのステップをプロファイリングすることもできます。この記事はかなり長くなったので、別の記事で。

今のところ、私はこれであなたを残します、楽しんでください..人々が立ち往生しているものがある場合はコメントを残してください.

クリス

于 2011-02-03T15:59:58.000 に答える
3

ネットワーク上のすべてのマシンからの応答が必要ですか、それともこれらの 300 台のマシンは大規模なネットワークのサブセットにすぎませんか?

すべてのマシンからの応答が必要な場合は、エコー要求にブロードキャスト アドレスまたはマルチキャスト アドレスを使用することを検討できます。

于 2011-02-03T14:27:52.483 に答える
2

ネットワークのすべてのノードに 1 つの ping を送信する Linux 用の "chknodes" 並列 ping を試してみてください。また、指定されている場合は、DNS 逆ルックアップと要求 http 応答も行います。これは完全に bash で書かれているため、簡単に確認したり、必要に応じて変更したりできます。ヘルプの印刷物は次のとおりです。

chknodes -h

chknodes ---- 高速パラレル ping

chknodes [-l|--log] [-h|--help] [-H|--http] [-u|--uninstall] [-v|--version] [-V|--verbose]

-l | --log ファイルへのログ -h | --help このヘルプ画面を表示する -H | --http http 応答もチェックします。--names ホスト名も取得します。--uninstall インストールを削除します。--version バージョンを表示 -V | --verbose ping された各 IP アドレスを表示します

それを実行するには、(sh/bash スクリプトと同様に) 実行権限を与える必要があります。

chmod +x chknodes

最初の実行時、つまり

./chknodes

/usr/local/bin/chknodes に自分自身をインストールすることを提案します。

chknodes

で十分です。ここで見つけることができます:

www.homelinuxpc.com/download/chknodes

于 2014-01-15T18:58:29.707 に答える