gpt4 book ai didi

multithreading - 是否可以使用 AsyncCalls 单元创建线程池?

转载 作者:行者123 更新时间:2023-12-03 14:48:15 25 4
gpt4 key购买 nike

我正在尝试使用 AsyncCalls 对整个 C 类子网执行 Netbios 查找。理想情况下,我希望它同时执行 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.

最佳答案

棘手的事情。我做了一些调试(嗯,相当多的调试),发现 AsyncCallsEx 中的代码块位于第 1296 行:

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

进一步挖掘表明它在 System.pas (_IntfCopy) 中的接口(interface)复制中阻塞

CALL    DWORD PTR [EAX] + VMTOFFSET IInterface._Release

查看相同代码的 pascal 版本,该行似乎释放了先前存储在目标参数中的引用计数。但是,目标是调用者(您的代码)中未使用的结果。

现在是棘手的部分。

AsyncCallEx 返回一个接口(interface)(在您的情况下)调用者会丢弃该接口(interface)。所以理论上编译后的代码(伪形式)应该是这样的

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

但是编译器将其优化为

loop
tmp := AsyncCallEx(...)
until
tmp._Release
为什么?因为它知道对接口(interface)进行赋值会自动释放tmp变量中存储的接口(interface)的引用计数(_IntfCopy中对_Release的调用)。因此无需显式调用 _Release。

但是,释放 IAsyncCall 会导致代码等待线程完成。所以基本上每次调用 AsyncCallEx 时都会等待前一个线程完成...

我不知道如何使用 AsyncCalls 很好地解决这个问题。我尝试了这种方法,但不知怎的,它并没有完全按预期工作(在 ping 大约 50 个地址后程序 block )。

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;

关于multithreading - 是否可以使用 AsyncCalls 单元创建线程池?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5671589/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com