对 Task.Run 使用 CancellationToken

use*_*648 1 c# task cancellationtokensource

我有一些Task.Run与取消令牌一起使用的代码。

这是我的代码:

public class TaskObject
{
    CancellationTokenSource _source = new CancellationTokenSource();
    public async Task TaskAction()
    {
        var task = Task.Run(async delegate
        {
            await TaskRun();
        }, _source.Token);

        //TaskCancel();

        try
        {
            task.Wait();
        }
        catch (Exception ex)
        {

        }
    }
    public async Task TaskRun()
    {
        if (_source.IsCancellationRequested)
        {
            _source.Token.ThrowIfCancellationRequested();
        }

        SpeechSynthesizer _speechSynthesizer = new SpeechSynthesizer();
        _speechSynthesizer.SpeakAsync("This is a test prompt");
    }
    public void TaskCancel()
    {
        _source.Cancel();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我调用TaskCancel()中的TaskAction(),则会捕获任务取消异常。

TaskCancel()如果我从对象外部调用,则不会捕获已取消的异常。

以下是一些代码,用于演示未捕获已取消异常的位置:

taskObject = new TaskObject();
await taskObject.TaskAction();
taskObject.TaskCancel();
Run Code Online (Sandbox Code Playgroud)

如何TaskCancel()从对象外部调用 以便捕获已取消的异常?

Kir*_*kiy 5

取消需要配合。Task.Run不知道调度的委托可能有什么副作用,因此Task.Run可以检查您的CancellationToken并抛出 OCE(不会使您的应用程序处于潜在不一致状态)的唯一逻辑上安全的点是在异步操作开始之前- 在该点之后直到用户代码来检查令牌。

问题是,您的用户代码大部分时间都在等待SpeechSynthesizer.SpeakAsync,而它没有接受CancellationToken.

SpeechSynthesizer但是,确实有一个SpeakAsyncCancelAll方法,您可以像这样插入它:

public async Task TaskRun(CancellationToken ct)
{
    ct.ThrowIfCancellationRequested();

    SpeechSynthesizer _speechSynthesizer = new SpeechSynthesizer();

    using (ct.Register(() => _speechSynthesizer.SpeakAsyncCancelAll())) {
        await _speechSynthesizer.SpeakAsync("This is a test prompt");
    }
}
Run Code Online (Sandbox Code Playgroud)

我无法告诉您这是否会产生指示操作已取消的异常 - 您需要亲自尝试看看。