【问题标题】:Using a CancellationToken for a Task.Run对 Task.Run 使用 CancellationToken
【发布时间】:2016-04-04 01:09:15
【问题描述】:

我有一些代码使用带有取消令牌的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();
    }
}

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

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

这里有一些代码来演示取消的异常没有被捕获:

taskObject = new TaskObject();
await taskObject.TaskAction();
taskObject.TaskCancel();

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

【问题讨论】:

    标签: c# task cancellationtokensource


    【解决方案1】:

    取消需要合作。 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");
        }
    }
    

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

    【讨论】:

    • 忽略 SpeechSynthesizer,我是否正确地说,当启动 Task.Run 时,除非在函数开始时检测到取消请求,否则我是否正确?
    • @user3736648,你似乎有正确的想法,但措辞需要一些润色(见第一段)。简而言之,Task.Run 在开始时检查一次CancellationToken,然后在线程池上执行您的委托。一旦委托调用正在进行中,Task.Run 将无能为力。
    • 谢谢。除了Task.Run之外,有没有正确的方法来创建可以随时取消的线程?
    • @user3736648,这在技术上是不可能的(因为它很难推断支持取消的系统的状态)。您想要的是协作取消(即原生支持取消的 API,例如我之前提到的 SpeakAsyncCancelAll 调用)。 MSDN:msdn.microsoft.com/en-us/library/dd997364(v=vs.110).aspx
    • 你当然可以放弃你的异步操作(在这种情况下是语音播放),但这正是它听起来的样子:即使@ 987654335@被取消:blogs.msdn.com/b/pfxteam/archive/2012/10/05/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-10
    相关资源
    最近更新 更多