2011-04-15 15 views
8

मैं AsyncCalls का उपयोग कर एक संपूर्ण कक्षा सी सबनेट पर नेटबीस लुकअप करने का प्रयास कर रहा हूं। आदर्श रूप में मैं इसे एक साथ 10+ लुकअप करना चाहता हूं लेकिन वर्तमान में यह केवल एक समय में 1 लुकअप करता है। मुझसे यहां क्या गलत हो रहा है?असिनकॉल यूनिट का उपयोग करके थ्रेड पूल बनाना संभव है?

मेरे फॉर्म में 1 बटन और 1 ज्ञापन है।

unit main; 

interface 

uses 
    Windows, 
    Messages, 
    SysUtils, 
    Classes, 
    Forms, 
    StdCtrls, 
    AsyncCalls, 
    IdGlobal, 
    IdUDPClient, 
    Controls; 

type 
    PWMUCommand = ^TWMUCommand; 

    TWMUCommand = record 
    host: string; 
    ip: string; 
    bOnline: boolean; 
    end; 

type 
    PNetbiosTask = ^TNetbiosTask; 

    TNetbiosTask = record 
    hMainForm: THandle; 
    sAddress: string; 
    sHostname: string; 
    bOnline: boolean; 
    iTimeout: Integer; 
    end; 

const 
    WM_THRD_SITE_MSG = WM_USER + 5; 
    WM_POSTED_MSG  = WM_USER + 8; 

type 
    TForm2 = class(TForm) 
    Button1: TButton; 
    Memo1: TMemo; 
    procedure Button1Click(Sender: TObject); 
    private 
    procedure ThreadMessage(var Msg: TMessage); message WM_POSTED_MSG; 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    Form2    : TForm2; 

implementation 

{$R *.dfm} 

function NetBiosLookup(Data: TNetbiosTask): boolean; 
const 
    NB_REQUEST  = #$A2#$48#$00#$00#$00#$01#$00#$00 + 
    #$00#$00#$00#$00#$20#$43#$4B#$41 + 
    #$41#$41#$41#$41#$41#$41#$41#$41 + 
    #$41#$41#$41#$41#$41#$41#$41#$41 + 
    #$41#$41#$41#$41#$41#$41#$41#$41 + 
    #$41#$41#$41#$41#$41#$00#$00#$21 + 
    #$00#$01; 

    NB_PORT   = 137; 
    NB_BUFSIZE  = 8192; 
var 
    Buffer   : TIdBytes; 
    I     : Integer; 
    RepName   : string; 
    UDPClient   : TIdUDPClient; 
    msg_prm   : PWMUCommand; 
begin 
    RepName := ''; 
    Result := False; 
    UDPClient := nil; 

    UDPClient := TIdUDPClient.Create(nil); 
    try 
    try 
     with UDPClient do 
     begin 
     Host := Trim(Data.sAddress); 
     Port := NB_PORT; 

     Send(NB_REQUEST); 
     end; 

     SetLength(Buffer, NB_BUFSIZE); 
     if (0 < UDPClient.ReceiveBuffer(Buffer, Data.iTimeout)) then 
     begin 

     for I := 1 to 15 do 
      RepName := RepName + Chr(Buffer[56 + I]); 

     RepName := Trim(RepName); 
     Data.sHostname := RepName; 

     Result := True; 
     end; 

    except 
     Result := False; 
    end; 
    finally 
    if Assigned(UDPClient) then 
     FreeAndNil(UDPClient); 
    end; 

    New(msg_prm); 
    msg_prm.host := RepName; 
    msg_prm.ip := Data.sAddress; 
    msg_prm.bOnline := Length(RepName) > 0; 

    PostMessage(Data.hMainForm, WM_POSTED_MSG, WM_THRD_SITE_MSG, integer(msg_prm)); 

end; 

procedure TForm2.Button1Click(Sender: TObject); 
var 
    i     : integer; 
    ArrNetbiosTasks : array of TNetbiosTask; 
    sIp    : string; 
begin 
    // 

    SetMaxAsyncCallThreads(50); 

    SetLength(ArrNetbiosTasks, 255); 
    sIp := '192.168.1.'; 
    for i := 1 to 255 do 
    begin 

    ArrNetbiosTasks[i - 1].hMainForm := Self.Handle; 
    ArrNetbiosTasks[i - 1].sAddress := Concat(sIp, IntToStr(i)); 
    ArrNetbiosTasks[i - 1].iTimeout := 5000; 

    AsyncCallEx(@NetBiosLookup, ArrNetbiosTasks[i - 1]); 
    Application.ProcessMessages; 
    end; 
end; 

procedure TForm2.ThreadMessage(var Msg: TMessage); 
var 
    msg_prm   : PWMUCommand; 
begin 
    // 
    case Msg.WParam of 
    WM_THRD_SITE_MSG: 
     begin 
     msg_prm := PWMUCommand(Msg.LParam); 
     try 
      Memo1.Lines.Add(msg_prm.ip + ' = ' + msg_prm.host + ' --- Online? ' + BoolToStr(msg_prm.bOnline)); 
     finally 
      Dispose(msg_prm); 
     end; 
     end; 
    end; 

end; 

end. 
+0

मुझे यहां कोई त्रुटि नहीं दिखाई दे रही है। क्या आप वाकई काम नहीं कर रहे हैं? –

+0

कोड एक नए थ्रेड में निष्पादित करता है, लेकिन यह एक ही समय में कई निष्पादित करने के बजाय 1 अनुरोध करता है। आदर्श रूप में, मैं इसे 10-50 कार्यकर्ता धागे को जन्म देना चाहता हूं और उन श्रमिकों को तब तक सभी कार्यों को पूरा करना है जब तक कोई भी नहीं छोड़ा जाता। – Mick

उत्तर

4

ट्रिकी सामान। मैं कुछ डिबगिंग किया (ठीक है, काफी कुछ डिबगिंग) और पता चला कि लाइन 1296 में AsyncCallsEx में कोड ब्लॉक:

Result := TAsyncCallArgRecord.Create(Proc, @Arg).ExecuteAsync; 

आगे खुदाई से पता चला कि यह

पर System.pas (_IntfCopy) में इंटरफ़ेस प्रतिलिपि में ब्लॉक
CALL DWORD PTR [EAX] + VMTOFFSET IInterface._Release 

उसी कोड के पास्कल संस्करण को देखते हुए ऐसा लगता है कि यह पंक्ति गंतव्य पैरामीटर में पहले संग्रहीत संदर्भ गणना जारी करती है। गंतव्य, हालांकि, एक परिणाम है जिसका उपयोग कॉलर (आपका कोड) में नहीं किया जाता है।

अब मुश्किल हिस्सा आता है।

AsyncCallEx एक इंटरफ़ेस देता है जो (आपके मामले में) कॉलर फेंकता है। तो सिद्धांत रूप में संकलित कोड (छद्म रूप में) इस

loop 
    tmp := AsyncCallEx(...) 
    tmp._Release 
until 

की तरह दिखना चाहिए हालांकि संकलक

loop 
    tmp := AsyncCallEx(...) 
until 
tmp._Release 

क्यों को यह अनुकूलित करता है? क्योंकि यह जानता है कि इंटरफ़ेस को असाइन करना स्वचालित रूप से tmp चर में संग्रहीत इंटरफ़ेस की संदर्भ गणना जारी करेगा (_IntfCopy में _Release को कॉल करें)। इसलिए स्पष्ट रूप से _Release को कॉल करने की आवश्यकता नहीं है।

IAsyncCall को जारी करने के कारण कोड थ्रेड पूर्ण होने पर प्रतीक्षा कर सकता है। इसलिए मूल रूप से आप प्रत्येक थ्रेड को पूरा करने के लिए प्रतीक्षा करते हैं जब आप AsyncCallEx को कॉल करते हैं ...

मुझे नहीं पता कि AsyncCalls का उपयोग करके इसे कैसे हल किया जाए। मैंने इस दृष्टिकोण की कोशिश की लेकिन किसी भी तरह से यह पूरी तरह से काम नहीं कर रहा है (लगभग 50 पतों को पिंग करने के बाद प्रोग्राम ब्लॉक)।

type 
    TNetbiosTask = record 
    //... as before ... 
    thread: IAsyncCall; 
    end; 

    for i := 1 to 255 do 
    begin 

    ArrNetbiosTasks[i - 1].hMainForm := Self.Handle; 
    ArrNetbiosTasks[i - 1].sAddress := Concat(sIp, IntToStr(i)); 
    ArrNetbiosTasks[i - 1].iTimeout := 5000; 

    ArrNetbiosTasks[i - 1].thread := AsyncCallEx(@NetBiosLookup, ArrNetbiosTasks[i - 1]); 
    Application.ProcessMessages; 
    end; 
    for i := 1 to 255 do // wait on all threads 
    ArrNetbiosTasks[i - 1].thread := nil; 
+0

आपके विस्तृत उत्तर के लिए धन्यवाद। ऐसा लगता है कि AsyncCalls इस कार्य के लिए सही उपकरण नहीं है। – Mick

3

आप AsyncCallEx() फोन या AsyncCalls कार्यों के किसी भी अन्य आप एक IAsyncCall इंटरफ़ेस सूचक लौटे रहे हैं। यदि इसका संदर्भ काउंटर 0 तक पहुंचता है तो अंतर्निहित वस्तु नष्ट हो जाती है, जो कार्यकर्ता थ्रेड कोड को पूरा करने की प्रतीक्षा करेगा। आप एक लूप में AsyncCallEx() पर कॉल कर रहे हैं, इसलिए प्रत्येक बार लौटाए गए इंटरफ़ेस पॉइंटर को उसी (छुपा) चर के लिए असाइन किया जाएगा, संदर्भ काउंटर को कम करेगा और इस प्रकार पिछले एसिंक्रोनस कॉल ऑब्जेक्ट को सिंक्रनाइज़ कर देगा।

इस हल करने के लिए बस, प्रपत्र वर्ग के लिए IAsyncCall के एक निजी सरणी जोड़ सकते हैं ताकि तरह: सरणी तत्वों के लिए

private 
    fASyncCalls: array[byte] of IAsyncCall; 

और आवंटित लौटे इंटरफ़ेस संकेत:

fASyncCalls[i] := AsyncCallEx(@NetBiosLookup, ArrNetbiosTasks[i - 1]); 

हो जाएगा ताकि इंटरफेस को जीवित रखें और समांतर निष्पादन को सक्षम करें।

ध्यान दें कि यह केवल सामान्य विचार है, आपको कॉल रिटर्न के दौरान संबंधित सरणी तत्व को रीसेट करने के लिए कोड जोड़ना चाहिए, और फ़ॉर्म को मुक्त करने से पहले सभी कॉल को पूरा करने की प्रतीक्षा करें।

संबंधित मुद्दे