TPL Dataflowをよく理解するための簡単なプログラムを作成しようとしています。私はそれが完了している場合、データフローブロックを再起動する長期実行タスクを作成しようとしています。フォールトステートでActionBlockが完了しない
私の現在のRestartActionBlock
タスクは、 "-1"と入力してブロックを完全に呼び出すと、ActionBlock
の完了を待つことができます。しかし、ブロックをフォルトするための例外を発生させようとしたり、ブロックFault()インタフェースメソッドを呼び出すと、ActionBlock
の完了タスクは完了しません。この状況では、await singleTestFlow.Completion;
コールは決して続かない。
例外を発生させた後、またはFault()メソッドを呼び出した後、プログラムに別の入力を入力し、ブロックがフォールト状態になっていることを確認するコードをデバッグすることによって、ブロックがフォルト状態になっていることを確認できます:ブロックが故障状態にある場合は、なぜawait singleTestFlow.Completion;
は
返すことはありませんでしたか?故障したブロックのデバッグスクリーンショットが撮影された場所
class Program
{
private static ActionBlock<string> singleTestFlow;
static void Main(string[] args)
{
//start thread that should restart a completed action block
Task.Run(RestartActionBlock);
Console.WriteLine("Enter -0 to exit, -1 to complete flow, -0- to throw an exception, Anything else otherwise");
var input = Console.ReadLine();
//allow user to input text until "-0" is entered
while (!input.Equals("-0"))
{
if (input.Equals("-1"))
{
singleTestFlow.Complete();
}
singleTestFlow.Post(input);
input = Console.ReadLine();
}
async Task RestartActionBlock()
{
var iterations = 0;
while (true)
{
singleTestFlow = new ActionBlock<string>(s =>
{
if (s.Equals("-0-"))
{
//throw new Exception("Something went wrong in here");
((IDataflowBlock)singleTestFlow).Fault(new Exception("Something went wrong in here"));
}
Console.WriteLine($"{iterations}: " + s);
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Environment.ProcessorCount });
await singleTestFlow.Completion;
var completionTask = singleTestFlow.Completion;
var message = $"action block: {iterations} ";
switch (completionTask.Status)
{
case TaskStatus.RanToCompletion:
message += "ran to completion";
break;
case TaskStatus.Canceled:
message += "was canceled";
break;
case TaskStatus.Faulted:
message += "has faulted";
Console.WriteLine(completionTask.Exception);
break;
}
Console.WriteLine(message);
iterations++;
}
}
}
}
大変ありがとうございました!それは今働いている。あなたの答えから、2つの新しい質問があります。 'singleTestFlow.Completion'と' Task.WhenAny(singleTestFlow.Completion) 'を待つ間の違いは何ですか?後者は私のコードを動作させるが、私はその違いを理解していない。次に、 'Task.Run(RestartActionBlock)'から返されたタスクをどのように使用できますか?私は、メインでコードの残りの部分を続ける必要があるので、その完了を待つ必要はありません。 – Slvrfn
'RestartActionBlock'タスクを永久に実行させたい場合は、包括的な例外処理が組み込まれていることを確認する必要があります。' await singleTestFlow.Completion'が例外をスローした場合(ブロック障害のため)、 'RestartActionBlock'は直ちに戻ります同期方法とよく似ています)。それは決して「待ち」を超えて進展しません。 'Task.WhenAny'は、最初に完了した(または失敗した)タスクへの参照を返すだけなので、回避することができます。例外を再スローしません。その効果は 'await singleTestFlow.Completion'の周りに' try/catch'を置くのと似ています。 –
'Task.Run(RestartActionBlock) 'によって返された' Task'への参照を保持していたのであれば、ある時点でFaulted状態に移行し、 'RestartActionBlock'ループが停止していなければならないことがわかります。これはデバッグに役立ちます。事実、問題を診断するために私が個人的にどのように成功したかは、まさにその通りです。 –