将CancellationToken作为参数传递给Task.Run有什么好处?

Dav*_*her 6 .net vb.net task task-parallel-library cancellation

显然,我意识到它使我可以取消任务,但是此代码无需将令牌传递到Task.Run就可以达到相同的效果。

实际区别是什么?谢谢。

Dim cts As New CancellationTokenSource
Dim ct As CancellationToken = cts.Token
Task.Run(Sub()
             For i = 1 To 1000
                 Debug.WriteLine(i)
                 ct.ThrowIfCancellationRequested()
                 Threading.Thread.Sleep(10)
             Next
         End Sub)

cts.CancelAfter(500)
Run Code Online (Sandbox Code Playgroud)

VS

Dim cts As New CancellationTokenSource
Dim ct As CancellationToken = cts.Token
Task.Run(Sub()
             For i = 1 To 1000
                 Debug.WriteLine(i)
                 ct.ThrowIfCancellationRequested()
                 Threading.Thread.Sleep(10)
             Next
         End Sub, ct)

cts.CancelAfter(500)
Run Code Online (Sandbox Code Playgroud)

Joe*_*ell 8

API文档Task.Run(Action, CancellationToken)具有以下说明:

如果在任务开始执行之前请求取消,则该任务不会执行。而是将其设置为Canceled状态,并引发TaskCanceledException异常。

因此,在您的情况下,没有任何实际区别,因为您在发出取消之前要等待500毫秒。在这段时间里,任务被调度,开始执行,并在发出取消之前多次运行循环,表现为引发的异常ct.ThrowIfCancellationRequested()

通过示例的修改后的版本,Task.Run(Action)和之间的区别Task.Run(Action, CancellationToken)更加明显:

Try
    Dim cts As New CancellationTokenSource
    Dim ct As CancellationToken = cts.Token

    cts.Cancel()

    Dim task As Task = Task.Run(
        Sub()
            Console.WriteLine("Started running your code!")
            ct.ThrowIfCancellationRequested()
            Console.WriteLine("Finished running your code!")
        End Sub, ct)

    task.Wait()

Catch ex As AggregateException
    Console.Error.WriteLine("Caught exception: " & ex.InnerException.Message)
End Try

Console.WriteLine("Done, press Enter to quit.")
Console.ReadLine()
Run Code Online (Sandbox Code Playgroud)

在这种情况下,Task.Run计划任务运行,但还要将取消令牌与该任务相关联。当我们调用时task.Wait(),在线程池执行任务之前,它会检查取消令牌并注意到已经对该令牌发出了取消,因此它决定在执行任务之前取消。因此输出为:

Caught exception: A task was canceled.
Done, press Enter to quit.
Run Code Online (Sandbox Code Playgroud)

如果改为End Sub, ct)使用End Sub),则线程池不会知道取消令牌,因此,即使您已发出取消通知,它也会继续执行任务,然后任务代码本身会检查取消情况。所以输出是:

Started running your code!
Caught exception: The operation was canceled.
Done, press Enter to quit.
Run Code Online (Sandbox Code Playgroud)

(您可以看到在这两种情况下,异常消息也略有不同。)

总而言之,向Task.Run方法提供取消令牌可以使线程池本身在线程池有机会执行任务之前知道任务是否被取消。这样,线程池甚至不必费心开始运行任务,从而节省了时间和资源。