.net - 如果无法轮询 CancellationToken,则提供取消

标签 .net vb.net task-parallel-library cancellation cancellation-token

这是一个阻塞调用者线程但不支持取消的方法的(愚蠢的)示例:

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

最佳答案

让我们改一下问题。您有一个名为 WorkUntil5 的不可取消操作,您希望将其取消。你怎么做到的?

Stephen Toub 在“How do I cancel non-cancelable async operations?”中讨论了这种情况。

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

你想:

  1. 真的要取消长操作,还是要
  2. 停止等待?

这两种操作都是不可靠的,因为您无法知道长时间运行的方法中还有哪些未完成。这不是 TPL 设计的问题,而是长时间运行方法的设计问题。

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

操作 #2 可以使用 Tasks 和 TaskCompletionSource 来处理,但不是很可靠,因为您无法知道长时间的操作是否遗留垃圾或异常终止。

Stephen 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; 
}

关于.net - 如果无法轮询 CancellationToken,则提供取消,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13794229/

相关文章:

regex - 如何查找时间值(类似 # :##) in a string

.net - 最小化时如何将.net应用程序放在系统托盘中?

.net - WPF 为 Silverlight/Windows Phone 7 设计自定义 UI 组件

vb.net - Sub 的关闭表格

vb.net - 表单控件数组上的线程安全调用

c# - 我可以嵌套 Parallel.Invoke 方法吗?

c# - C# 中的任务 - 使用 Random 的结果不明确

c# - 线程池会不会排队一个定时器的回调函数,有时会同时调度多个线程?

c# - 如何在 ApplicationUserManager 上使用 Identity 2.2 并且不使用 ILookupNormalizer

.net - 404 - 在 Controller Y 上找不到公共(public)操作方法 X(ActionInvoker.InvokeAction 返回 false)