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.