首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >CancellationTokenSource.Cancel()挂起

CancellationTokenSource.Cancel()挂起
EN

Stack Overflow用户
提问于 2018-09-21 20:11:30
回答 1查看 1.3K关注 0票数 3

当其中一个异步处于活动循环中时,我正在观察CancellationTokenSource.Cancel中的挂起。

完整代码:

代码语言:javascript
复制
static async Task doStuff(CancellationToken token)
{
    try
    {
        // await Task.Yield();
        await Task.Delay(-1, token);
    }
    catch (TaskCanceledException)
    {
    }

    while (true) ;
}

static void Main(string[] args)
{
    var main = Task.Run(() =>
    {
        using (var csource = new CancellationTokenSource())
        {
            var task = doStuff(csource.Token);
            Console.WriteLine("Spawned");
            csource.Cancel();
            Console.WriteLine("Cancelled");
        }
    });
    main.GetAwaiter().GetResult();
}

打印Spawned和悬挂。Call堆栈看起来像:

代码语言:javascript
复制
ConsoleApp9.exe!ConsoleApp9.Program.doStuff(System.Threading.CancellationToken token) Line 23   C#
[Resuming Async Method] 
[External Code] 
ConsoleApp9.exe!ConsoleApp9.Program.Main.AnonymousMethod__1_0() Line 34 C#
[External Code] 

不使用await Task.Yield将导致输出Spawned\nCancelled

知道为什么吗?C#是否保证一旦生成的异步不会阻止其他异步?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-09-21 23:01:30

CancellationTokenSource没有任何任务调度器的概念。如果回调没有用自定义同步上下文注册,CancellationTokenSource将在与.Cancel()相同的调用堆栈中执行它。在您的示例中,取消回调完成了Task.Delay返回的任务,然后继续内联,从而在CancellationTokenSource.Cancel中产生一个无限循环。

使用Task.Yield的示例之所以有效,只是因为存在竞争条件。当令牌被取消时,线程尚未开始执行Task.Delay,因此不能继续内联。如果您更改Main以添加暂停,您将看到即使使用Task.Yield,它仍然会冻结

代码语言:javascript
复制
static void Main(string[] args)
{
    var main = Task.Run(() =>
    {
        using (var csource = new CancellationTokenSource())
        {
            var task = doStuff(csource.Token);
            Console.WriteLine("Spawned");

            Thread.Sleep(1000); // Give enough time to reach Task.Delay

            csource.Cancel();
            Console.WriteLine("Cancelled");
        }
    });
    main.GetAwaiter().GetResult();
}

现在,保护对CancellationTokenSource.Cancel的调用的唯一可靠方法是将其包装在Task.Run中。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52450528

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档