【问题标题】:CancellationTokenSource not working as expected on a TaskCompletionSourceCancellationTokenSource 在 TaskCompletionSource 上未按预期工作
【发布时间】:2020-09-20 13:03:50
【问题描述】:

我正在尝试使用 TaskCompletionSource 将基于事件的调用包装到任务上,但由于某种原因,这无法按预期工作,当使用 await SendRequest(cmdid, data) 调用 SendRequest 时,无论我多么努力地尝试 SetCanceled,它都不会返回,设置结果等...

为了确保任务在进一步排除故障之前结束,下面我正在尝试使用 CancellationTokenSource,它似乎没有按预期工作,我是否误解了 TaskCompletionSource 和 Cancellation 的概念和用法?

我的理解是,当附加到 tcs 的取消令牌将在 1 秒后过期时,以下代码会引发异常,但不会,await SendRequest(cmdid, data) 会永远阻塞。

        public async Task<object> SendRequest(CmdId cmdID, object data)
        {
            CancellationTokenSource source = new CancellationTokenSource(1000);
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(source.Token);
            
            Request(cmdID, seqNr, data);
 
            return await tcs.Task;
        }

提前感谢您的帮助。

【问题讨论】:

  • 你确定不是你的请求方法阻塞了吗?

标签: c# .net async-await


【解决方案1】:

这种用法:

TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(source.Token);

只需设置任务的AsyncState。它与取消无关,并且不会自动挂钩。如果你想让取消做某事,你需要在取消令牌上使用Register方法,并实际触发TCS被取消。

【讨论】:

    【解决方案2】:

    以下行不是您所期望的:

    TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(source.Token);
    

    这会调用TaskCompletionSource(object state) 构造函数,它只是设置Task.AsyncState 属性,这是旧的IAsyncResult.AsyncState 实现,与取消无关。

    TaskCompletionSource&lt;TResult&gt; 可以简单地创建未完成的任务并手动设置其结果(或取消/错误状态),Task 本身不公开这些功能。

    在这一行:

    return await tcs.Task;
    

    等待将永远不会返回,因为您从未设置结果。

    解决方案 1:

    如果Request 是一个阻塞的同步调用,您可以简单地将主体替换为如下内容:

    return Task.Run(() => Request(cmdID, seqNr, data, source.Token));
    

    请注意:

    • 这不是一种优雅的异步方式,因为它使用池线程来执行Request
    • 我添加了一个新的CancellationToken 参数,您需要经常轮询该参数

    解决方案 2:

    如果您真的想使用TaskCompletionSource,最好将其设为字段,开始发送请求,订阅响应的任何事件,您可以相应地设置结果并立即返回创建的未完成tcs.Task。我不会复制粘贴我之前的答案,但here 你可以找到示例。

    【讨论】:

    • 非常感谢!现在这似乎运作良好,我更了解 TCS 的用法。为什么我在 TCS 构造函数中使用了取消标记,不知道,我想我在某个地方看错了。
    【解决方案3】:

    是的,对于TaskCompletionSourceCancellationToken 之间的关系,肯定存在误解。 TaskCompletionSource 用于创建可以手动和直接控制的Task。不支持使用另一个控制器 (CancellationTokenSource) 控制控制器,因为它没有任何意义。没有带有CancellationToken 参数的TaskCompletionSource 构造函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-06
      • 1970-01-01
      • 1970-01-01
      • 2017-02-13
      • 2018-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多