问题
Using the OmniThreadLibrary and Delphi XE4, I am hoping to run multiple threads that process data in the background, adding speed increases to my already existing code.
When calling the procedure below, the Application GUI stops processing any input until all of the threads have completed. My understanding is that using .NoWait
should allow the procedure to exit even when the threads are running.
procedure Test(input: TStringList; output: TList<TMaintFore>);
var
outQueue: IOmniBlockingCollection;
transaction: TOmniValue;
begin
outQueue := TOmniBlockingCollection.Create;
Parallel.ForEach(0, input.Count - 1)
.NoWait
.Into(outQueue)
.Execute(
procedure(const value: integer; var result: TOmniValue)
begin
result := TMaintFore.Create(input[value]);
end
);
end;
Is my understanding of the ForEach loop incorrect, suggesting I should use an alternate method to achieve background processing? Any suggestions on the correct use of the OmniThreadLibrary is appreciated.
回答1:
You have to store the interface returned from the Parallel.ForEach in a global (form etc) variable and destroy it only when the ForEach finishes execution.
In your example, the result of ForEach is stored in a temporary variable which is destroyed when the Test procedure exits. The ForEach destructor waits for all tasks to complete and that blocks your program.
The safest (but admittedly non-obvious) way to destroy the foreach interface on task completion is to use the OnStop method and from it queue a command to the main thread.
var
loop: IOmniParallelLoop<integer>;
loop := Parallel.ForEach(1, N).NoWait;
loop.OnStop(
procedure (const task: IOmniTask)
begin
task.Invoke(
procedure
begin
// do anything
loop := nil;
end);
end);
loop.Execute(
procedure (const value: integer)
begin
...
end);
This is documented in the wiki.
来源:https://stackoverflow.com/questions/16880457/why-is-omnithreadlibrarys-foreach-blocking-main-thread