Shrinavat 16 Posted September 4 I'm using the Async abstraction from the OmniThreadLibrary to ping multiple hosts in background threads (one thread per host). In the main thread, I want to perform some actions with the ping results based on the host ID. Here's a simplified version of my code: procedure Test; var arr_id: TArray<LongWord>; arr_ip: TArray<string>; begin arr_id := [1,2,3,4,5,6,7]; arr_ip := ['192.168.16.1','192.168.16.2','192.168.16.3','192.168.16.4','192.168.16.5','192.168.16.6','192.168.16.7']; for var i := Low(arr_id) to High(arr_id) do begin Parallel.Async( procedure (const task: IOmniTask) var PingResult: Boolean; rec_id: LongWord; rec_ip: string; begin // background thread rec_id := arr_id[i]; rec_ip := arr_ip[i]; // PingResult := PingHost(rec_ip); // in the main thread task.Invoke( procedure begin // some actions with ping results based on host ID mmoLog.Lines.Add(Format('rec_id=%d, rec_ip=%s', [rec_id, rec_ip])); end); end); end; end; I'm getting inconsistent results. For example: rec_id=3, rec_ip=192.168.16.3 rec_id=3, rec_ip=192.168.16.3 rec_id=135299280, rec_ip=烬Ŧ rec_id=135299280, rec_ip=烬Ŧ rec_id=135299280, rec_ip=烬Ŧ rec_id=135299280, rec_ip=烬Ŧ rec_id=135299280, rec_ip=烬Ŧ or rec_id=2, rec_ip=192.168.16.2 rec_id=3, rec_ip=192.168.16.3 rec_id=4, rec_ip=192.168.16.4 rec_id=7, rec_ip=192.168.16.7 rec_id=7, rec_ip=192.168.16.7 rec_id=7, rec_ip=192.168.16.7 rec_id=135299280, rec_ip=烬Ŧ Not all IPs are being pinged, and some are pinged multiple times. Also, I get garbage data in some cases. How can I achieve the desired behavior, where each IP is pinged once and the results are processed correctly in the main thread? Alternatively, how can I rewrite the code using TTask? Share this post Link to post
Lorenzo B 1 Posted September 4 Hello, I think the problem is in the use of the variable "i" inside the async method. You have to make the variable "i" enter the procedure as a parameter of the procedure itself! Something like: for var i := Low(arr_id) to High(arr_id) do begin Parallel.Async( procedure (const task: IOmniTask) begin DOPING(task,i); end); end; procedure DOPING(const task: IOmniTask;i:integer) var PingResult: Boolean; rec_id: LongWord; rec_ip: string; begin // background thread rec_id := arr_id[i]; rec_ip := arr_ip[i]; // PingResult := PingHost(rec_ip); // in the main thread task.Invoke( procedure begin // some actions with ping results based on host ID mmoLog.Lines.Add(Format('rec_id=%d, rec_ip=%s', [rec_id, rec_ip])); end); end; This I think should solve it (I can't check now), however it can also be done in many other ways. 1 Share this post Link to post
Shrinavat 16 Posted September 5 The issue stemmed from how variables were captured within the closure passed to Parallel.Async. The loop variable i was not correctly captured, leading to incorrect values being used in the background tasks. The following code demonstrates my working solution: procedure Test; var arr_id: TArray<LongWord>; arr_ip: TArray<string>; procedure CreatePingTask(const id: LongWord; const ip: string); begin Parallel.Async( procedure (const task: IOmniTask) var PingResult: Boolean; begin // background thread // Simulate ping PingResult := Random(2) = 1; // random result for demonstration Sleep(Random(5000)); // simulate ping delay // in the main thread task.Invoke( procedure begin mmoLog.Lines.Add(Format('PingResult=%s, rec_id=%d, rec_ip=%s', [PingResult.ToString, id, ip])); end); end); end; begin arr_id := [1,2,3,4,5,6,7]; arr_ip := ['192.168.16.1','192.168.16.2','192.168.16.3','192.168.16.4','192.168.16.5','192.168.16.6','192.168.16.7']; for var i := Low(arr_id) to High(arr_id) do CreatePingTask(arr_id[i], arr_ip[i]); end; Explanation: To ensure each background task receives the correct ID and IP, we pass them as parameters to the CreatePingTask procedure. This creates a separate copy of the variables for each task. task.Invoke is used to synchronize with the main thread and update the UI with the ping results. This approach ensures that each host is pinged exactly once and the results are processed correctly in the main thread. I'd love to get some expert opinions on my solution. Are there any "gotchas" I should be watching out for? Share this post Link to post
FredS 138 Posted September 7 You can make a hash of the IP and include that in the payload of the IcmpSendEcho2 call. Then compare those with the return payload and if they match you are done. Else store that info in an array or pass it to a callback to check in another/main thread. Share this post Link to post