提供了消除,如果投票的CancellationToken是不可能的(Providing cancel

2019-08-03 19:04发布

下面是阻塞调用者的线程,但不支持取消的方法的(傻)例如:

Public Sub WorkUntil5()
    Threading.SpinWait.SpinUntil(Function() Now.Hour >= 17)
End Sub

在最坏的情况下,调用此方法需要17小时才能返回。 假装我没有访问该方法的源代码。 如何包装在需要的CancellationToken的方法调用?

我们的目标是让WorkUntil5()运行,直至取消请求。 在这一点上调用应该使用任何可能的手段来终止。

这是我能拿出自己的最好方式。 它使用的任务,但它仍然阻塞调用者的线程。 有关的东西感觉不对。 思考应该有更好的方式来调用mres.Set()一旦第一个调用返回。

Public Sub WorkUntilYouGetBored(cancellationToken As Threading.CancellationToken)
    Dim mres As New Threading.ManualResetEventSlim

    Using cancellationToken.Register(Sub() mres.Set())
        Dim t = Task.Factory.StartNew(Sub() WorkUntil5())
        t.ContinueWith(Sub() mres.Set())

        mres.Wait()
    End Using

    If cancellationToken.IsCancellationRequested Then
        Console.WriteLine("You went home early.")
    Else
        Console.WriteLine("It's time to go home.")
    End If
End Sub

Answer 1:

让我们改一下这个问题。 您有一个名为WorkUntil5要取消不取消操作。 你怎么能这样做呢?

斯蒂芬Toub讨论了在这种情况下“ 如何取消不取消异步操作? ”。

唯一真正的解决方法是修复WorkUntil5,以便它允许取消。 如果这是不可能的(为什么?),你必须决定你的“取消”是什么意思?

你想要_____吗:

  1. 真的取消了长时间的操作,或者你想
  2. 停止等待呢?

这两种操作是不可靠的,因为你没有办法知道什么是在长时间运行的方法半途而废。 这不是与TPL的设计,但长期运行方法的设计有问题。

操作#1是不是真的有可能,因为该方法不提供任何取消它。

操作#2能够使用任务和TaskCompletionSource处理,但也不是很可靠的,因为你无法知道如果长时间操作留下的垃圾背后或有异常终止的方式。

斯蒂芬Toub展示了如何做到这一点,甚至提供了一个扩展方法,这样做:

public static async Task<T> WithCancellation<T>( 
this Task<T> task, CancellationToken cancellationToken) 
{ 
    var tcs = new TaskCompletionSource<bool>(); 
    using(cancellationToken.Register( 
            s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs)) 
        if (task != await Task.WhenAny(task, tcs.Task)) 
            throw new OperationCanceledException(cancellationToken); 
    return await task; 
}


文章来源: Providing cancellation if polling CancellationToken is not possible