很多年前,在旧论坛上,我问Primozh,如果Pipeline模式可以成为Uroboros,将半完整的结果反馈给自己。
当时Primozh表示这将是直截了当的,而PipeLine阶段不仅可以将OmniValues提供给OUTPUT,还可以输入INPUT。
问题是最初的喂食阶段运行速度太快,它们会过期并密封INPUT集合,因此无法联合密封它,因此只要它们尝试将半烤包送回自身 - 瞧! - OTL抛出“无法添加到已完成的集合”例外。
那么,如何通过自我馈送Pipeline
模式实现上述链接的自我爆炸任务?
{$A+} // not $A8
type FactTask = record
Goal, Curr: Cardinal;
Value : Int64;
end;
procedure TForm6.Button1Click(Sender: TObject);
var Msg: string;
f: FactTask;
Results: TArray<Int64>;
pipeOut: IOmniBlockingCollection;
pipe: IOmniPipeline;
begin
lblResults.Caption := ' WAIT, we are producing...';
Repaint;
pipe := Parallel.Pipeline;
f.Goal := edLen.Value; // 10
f.Curr := 0;
f.Value := 1;
pipe.Stage(
procedure ( const input, output: IOmniBlockingCollection )
begin
output.Add( TOmniValue.FromRecord( f ) );
end
);
pipe.Stage(
procedure ( const input, output: IOmniBlockingCollection )
var f_in, f_out: FactTask; v: TOmniValue;
begin
for v in input do begin
f_in := v.ToRecord<FactTask>;
if f_in.Curr < f_in.Goal then begin
f_out.Goal := f_in.Goal;
f_out.Curr := Succ(f_in.Curr);
f_out.Value := f_in.Value * f_out.Curr;
input.Add( TOmniValue.FromRecord( f_out ) ); // <<< Exception!
end;
end;
end
);
pipe.Stage(
procedure ( const input, output: IOmniBlockingCollection )
var f_in: FactTask; v: TOmniValue;
begin
for v in input do begin
f_in := v.ToRecord<FactTask>;
if f_in.Curr = f_in.Goal then begin
Output.Add( f_in.Value );
end;
end;
end
);
pipe.Run;
pipeOut := pipe.Output;
// pipe.WaitFor(INFINITE); ToArray would efficiently do that
// pipeOut.CompleteAdding; ...without frozing on Pipeline/Collections SetThrottle
Results := TOmniBlockingCollection.ToArray<Int64>(pipeOut);
Msg := IntToStr(f.Goal) + '! = ' + IntToStr(Results[0]);
lblResults.Caption := Msg;
ShowMessage(Msg);
end;
它与管道阶段崩溃,试图重新填充意外被TOmniPipeline.Run
封锁的输入。
在标记的行中,意外地抛出了“无法添加到竞争集合”的异常。
当集合在空和少之间保持平衡时,如何保持管道运行(这不仅是起始条件,它会在计算结束时重复)?
有点做梦:https://plus.google.com/+AriochThe/posts/LCHnSCmZYtx
更多:https://github.com/gabr42/OmniThreadLibrary/issues/61
答案 0 :(得分:2)
您的演示程序可以正常工作。
你的第一个阶段只输出一个记录到它的输出(顺便说一句,你可以通过写入pipe.Input
在主线程中完成),然后关闭它的输出管道。
这反过来导致第二阶段关闭。在for v in input
退出之前,第二阶段通常会尝试写入input
(除非您对计时非常满意)。但是,input
已经关闭,Add
会引发异常。调用TryAdd
代替Add
将解决此问题。
我猜想Pipeline
并不是你想要的抽象,而是通过使用别的东西会更好。我会使用正常的低级任务包装TOmniBlockingCollection
(对于第2阶段)。您必须使用Create(1)
创建此阻止集合,以便它知道它正在自行提供并自动取消阻止。 (有关详细信息,请参阅Parallel Search in a Tree中的the book示例。)