That's it, here's the design / best practice question for the difficult case of canceling Task: s in C #. How do you cancel a common task?
As a minimal example, let’s say the following: we have a long-running, joint operation “Work”. It takes a cancellation token as an argument and returns if it was canceled. It works in a certain state of the application and returns a value. Its result is independently required by two user interface components.
As long as the application state does not change, the value of the Work function should be cached, and if one calculation continues, the new query should not start the second calculation, but rather will wait for the result.
Any of the user interface components should be able to cancel its Task, without affecting the other task of the user interface components.
Are you still with me
The above can be accomplished by entering a task cache that wraps the real Work Task in TaskCompletionSources, whose task: s is then returned to the user interface components. If the user interface component cancels its "Task", it leaves only the TaskCompletionSource task and not the main task. This is all good. The user interface components create the CancellationSource, and the cancellation request is a normal design from top to bottom, and below is a working TaskCompletionSource Task.
Now, to the real problem. What to do when the state of the application changes? Assume that using the Work function on a state copy is not possible.
One solution would be to listen for a state change in the task cache (or there). If the cache has a CancellationToken application used by the main task, the one that performs the Work function can cancel it. Then this can lead to the cancellation of all connected TaskCompletionSources Task: s, and thus both components of the user interface will receive the canceled tasks. This is a kind of failure from the bottom up.
Is there a preferred way to do this? Is there a design pattern that describes it somewhere?
A bottom-up reduction can be implemented, but this is a bit strange. A UI task is created using a CancellationToken, but is canceled due to another (internal) CancellationToken. In addition, since the tokens do not match, the OperationCancelledException cannot simply be ignored in the user interface, which (ultimately) will cause the exception to be thrown in the external Task: s finalizer.