How to force an ActionBlock to complete fast

后端 未结 1 983
[愿得一人]
[愿得一人] 2021-01-28 16:40

According to the documentation:

A dataflow block is considered completed when it is not currently processing a message and when it has guaranteed that it

相关标签:
1条回答
  • 2021-01-28 17:04

    Based on this excerpt from your comment...:

    What I want to happen in case of a cancellation request is to ignore the currently running workitem. I don't care about it any more, so why I have to wait for it?

    ...and assuming you are truly OK with leaving the Task running, you can simply wrap the job you wish to call inside another Task which will constantly poll for cancellation or completion, and cancel that Task instead. Take a look at the following "proof-of-concept" code that wraps a "long-running" task inside another Task "tasked" with constantly polling the wrapped task for completion, and a CancellationToken for cancellation (completely "spur-of-the-moment" status, you will want to re-adapt it a bit of course):

    public class LongRunningTaskSource
    {
        public Task LongRunning(int milliseconds)
        {
            return Task.Run(() =>
            {
                Console.WriteLine("Starting long running task");
                Thread.Sleep(3000);
                Console.WriteLine("Finished long running task");
            });
        }
    
        public Task LongRunningTaskWrapper(int milliseconds, CancellationToken token)
        {
            Task task = LongRunning(milliseconds);
    
            Task wrapperTask = Task.Run(() =>
            {
                while (true)
                {
                    //Check for completion (you could, of course, do different things
                    //depending on whether it is faulted or completed).
                    if (!(task.Status == TaskStatus.Running))
                        break;
    
                    //Check for cancellation.
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("Aborting Task.");
    
                        token.ThrowIfCancellationRequested();
                    }
                }
    
            }, token);
    
            return wrapperTask;
        }
    }
    

    Using the following code:

    static void Main()
    {
        LongRunningTaskSource longRunning = new LongRunningTaskSource();
    
        CancellationTokenSource cts = new CancellationTokenSource(1500);
    
        Task task = longRunning.LongRunningTaskWrapper(3000, cts.Token);
    
        //Sleep long enough to let things roll on their own.
        Thread.Sleep(5000);
    
        Console.WriteLine("Ended Main");
    }
    

    ...produces the following output:

    Starting long running task
    Aborting Task.
    Exception thrown: 'System.OperationCanceledException' in mscorlib.dll
    Finished long running task
    Ended Main
    

    The wrapped Task obviously completes in its own good time. If you don't have a problem with that, which is often not the case, hopefully, this should fit your needs.

    As a supplementary example, running the following code (letting the wrapped Task finish before time-out):

    static void Main()
    {
        LongRunningTaskSource longRunning = new LongRunningTaskSource();
    
        CancellationTokenSource cts = new CancellationTokenSource(3000);
    
        Task task = longRunning.LongRunningTaskWrapper(1500, cts.Token);
    
        //Sleep long enough to let things roll on their own.
        Thread.Sleep(5000);
    
        Console.WriteLine("Ended Main");
    }
    

    ...produces the following output:

    Starting long running task
    Finished long running task
    Ended Main
    

    So the task started and finished before timeout and nothing had to be cancelled. Of course nothing is blocked while waiting. As you probably already know, of course, if you know what is being used behind the scenes in the long-running code, it would be good to clean up if necessary.

    Hopefully, you can adapt this example to pass something like this to your ActionBlock.

    Disclaimer & Notes

    I am not familiar with the TPL Dataflow library, so this is just a workaround, of course. Also, if all you have is, for example, a synchronous method call that you do not have any influence on at all, then you will obviously need two tasks. One wrapper task to wrap the synchronous call and another one to wrap the wrapper task to include continuous status polling and cancellation checks.

    0 讨论(0)
提交回复
热议问题