Best Practice for ITargetBlock <TInput> .Completion.ContinueWith ()
This question is about best practices when using ContinueWith() to handle the completion of a TPL data block.
The ITargetBlock<TInput>.Completion() method allows asynchronously processing completion of the database creation using ContinueWith() .
Consider the following Console application code, which demonstrates very simple use:
private static void Main() { test().Wait(); } static async Task test() { var transform = new TransformBlock<int, double>(i => i/2.0); var output = new ActionBlock<double>(d => Console.WriteLine(d)); // Warning CS4014 here: transform.Completion.ContinueWith(continuation => output.Complete()); transform.LinkTo(output); for (int i = 0; i < 10; ++i) await transform.SendAsync(i); transform.Complete(); await output.Completion; } The code has a very simple TransformBlock , which divides integers by 2.0 and turns them into doubles. The converted data is processed using ActionBlock , which simply displays the values ββin the console window.
Conclusion:
0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 When the TransformBlock complete, I want to populate the ActionBlock as well. It is convenient to do so:
transform.Completion.ContinueWith(continuation => output.Complete()); And here is the problem. Since this is inside the async method, and I ignore the return value from ContinueWith() , I get a compiler warning:
warning CS4014: because this call is not expected, execution of the current method continues until the call is completed. Consider applying the wait statement to a call result.
It is clear that I cannot await call, as warned by a warning - if I do, the code freezes because the CompleteWith() task cannot be completed until transform.Complete() is called.
Now I have no problem handling the warning itself (I can simply suppress it or assign the task to a variable or use the extension of the task .Forget() , etc.) - but here are my questions:
- Is it safe to handle the associated completion of a DataBlock this way?
- Is there a better way?
- Is there anything important here that I missed?
I think that if I continued to do anything other than output.Complete() inside the continuation, I would have to provide exception handling - but maybe even just calling output.Complete() fraught with danger?
While you can implement distribution distribution yourself, TPL Dataflow does this for you with DataflowLinkOptions.PropagateCompletion :
transform.LinkTo(output, new DataflowLinkOptions {PropagateCompletion = true}); If you still want to implement this yourself, you can solve the problem to save the task and waiting for it when using Task.WhenAll :
static async Task test() { var transform = new TransformBlock<int, double>(i => i/2.0); var output = new ActionBlock<double>(d => Console.WriteLine(d)); // Warning CS4014 here: var continuation = transform.Completion.ContinueWith(_ => output.Complete()); transform.LinkTo(output); for (int i = 0; i < 10; ++i) await transform.SendAsync(i); transform.Complete(); await Task.WhenAll(continuation, output.Completion) } This allows you to handle any exceptions in output.Complete and remove the warning.