如何获得 Task.WhenAny 对 Task 和 CancellationToken 的效果?

2023-11-25

我有一个交互式任务,在“最糟糕”的情况下根本不会执行,因此它由TaskCompletionSource.

我想等待此任务完成,或者我收到的令牌被取消 - 以先发生者为准。完成此类工作的完美工具是Task.WhenAny,唯一的问题是它只需要任务,而我有一个Task和一个CancellationToken.

如何等待(异步,例如Task.WhenAny)对于触发的第一个事件 - 已完成的任务,还是已取消的令牌?

async Task MyCodeAsync(CancellationToken token)
{
  var tcs = new TaskCompletionSource<UserData>(); // represents interactive part

  await Task.WhenAny(tcs.Task, token); // imaginary call

  UserData data = tcs.Task.Result; // user interacted, let's continue
  ...
}

我不创建/管理令牌,因此无法更改它。我必须处理它。

Update:对于这种特殊情况,可以使用Register令牌上的方法来取消TaskCompletionSource。有关更通用的方法,请参阅 Matthew Watson 的回答。


您可以创建一个额外的任务,当取消令牌的等待句柄发出信号时返回:

var factory = new CancellationTokenSource();
var token   = factory.Token;

await Task.WhenAny(
    Task.Run(() => token.WaitHandle.WaitOne()),
    myTask());

(但是,请注意,这虽然简单,但确实会消耗额外的线程,这显然不理想。请参阅稍后的不使用额外线程的替代解决方案。)

如果您想检查哪个任务已完成,则必须在调用之前保留任务的副本WhenAny()因此您可以将它们与返回值进行比较,例如:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main()
        {
            var factory = new CancellationTokenSource(1000); // Change to 3000 for different result.
            var token   = factory.Token;
            var task    = myTask();

            var result = await Task.WhenAny(
                Task.Run(() => token.WaitHandle.WaitOne()),
                task);

            if (result == task)
                Console.WriteLine("myTask() completed");
            else
                Console.WriteLine("cancel token was signalled");
        }

        static async Task myTask()
        {
            await Task.Delay(2000);
        }
    }
}

如果您不想浪费整个线程等待取消令牌发出信号,您可以使用CancellationToken.Register()注册一个回调,您可以使用它设置结果TaskCompletionSource:

(从这里举起)

public static Task WhenCanceled(CancellationToken cancellationToken)
{
    var tcs = new TaskCompletionSource<bool>();
    cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).SetResult(true), tcs);
    return tcs.Task;
}

然后您可以按如下方式使用它:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main()
        {
            var factory = new CancellationTokenSource(1000);
            var token   = factory.Token;
            var task    = myTask();

            var result = await Task.WhenAny(
                WhenCanceled(token),
                task);

            if (result == task)
                Console.WriteLine("myTask() completed");
            else
                Console.WriteLine("cancel token was signalled");
        }

        public static Task WhenCanceled(CancellationToken cancellationToken)
        {
            var tcs = new TaskCompletionSource<bool>();
            cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).SetResult(true), tcs);
            return tcs.Task;
        }

        static async Task myTask()
        {
            await Task.Delay(2000);
        }
    }
}

对于一般情况,这是一种更好的方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何获得 Task.WhenAny 对 Task 和 CancellationToken 的效果? 的相关文章

随机推荐

  • 字符串大写 - 更好的方法

    哪种资本化方法更好 mine char charArray string toCharArray charArray 0 Character toUpperCase charArray 0 return new String charArr
  • 按字母顺序排列 Unicode 中的阿拉伯语和日语文本?

    有谁有 Unicode 中的按字母顺序排列阿拉伯语和日语文本的代码吗 如果代码是用 ruby 写的那就太好了 Unicode 代码点不是按字母顺序列出的 例如 Z Unicode 排序算法它们也是特定于语言的排序 法语顺序与德语或捷克语顺序
  • 在第三方服务器上验证 Android 的 authToken

    我正在编写一个 Android 应用程序 它使用 AccountManager 来获取令牌 我可以通过 Android 应用程序与 Google Picasa 进行交互 它工作得很好 我想要实现的目标如下 将一些文本 authToken 发
  • Javascript 检测系统的音量(声音)和插入的音频插孔

    HTML5 具有板载音量检测功能
  • SQL Server BIGINT 或 DECIMAL(18,0) 主键

    我们有一个 SQL Server 2005 数据库 我们希望提高批量删除 插入 选择的性能 我注意到它使用decimal 18 0 为其主键 我知道这会给我们带来更多的价值bigint但我希望这可能是一个快速的胜利 并且根据我的计算 应该能
  • 无法解析“:app@debug/compileClasspath”的依赖关系:无法解析com.android.support:appcompat-v7:26.1.0

    无法解析 app debug compileClasspath 的依赖关系 无法解析com android support appcompat v7 26 1 0 无法解析 com android support appcompat v7
  • 更新代码生成 6.0.9 后无法加载项目 X 的​​信息

    我正在开发一个 NET Core 项目 昨天 Web CodeGeneration已自动更新 更新后 当我尝试向项目添加新视图时出现错误 脚手架失败 无法加载项目 X 的 信息 我尝试再次删除并重新安装所有 nuget 软件包 我检查了软件
  • wcf 尝试设置跟踪以进行调试,而不是写入日志文件

    这是我的 web config 在 IIS7 上的应用程序中运行 WCF 服务 但没有任何内容写入指定文件 已向所有人授予对该文件的权限
  • 使用GCC编译C代码

    我在我的电脑上安装了 MinGWWindows8 笔记本电脑并尝试编译 C 代码文件 gcc test c o test exe 编译器没有给出警告或错误 但没有创建 test exe 我如何让编译器创建文件 test c My termi
  • 如何在 Django 1.4 中存储简单的日期时间

    我有一个格式为 2012 05 19 19 13 00 的简单日期和时间 需要使用 Django 1 4 及其时区感知功能来存储它 尽管无法知道日期最初位于哪个时区 但将其存储为 UTC 似乎是有意义的 但是 使用 pytz 等 我不确定如
  • 将整数格式化为十六进制字符串

    我需要从随机整数 0 255 列表中创建一串十六进制数字 每个十六进制数字应由两个字符表示 5 05 16 10 等 Example Input 0 1 2 3 127 200 255 Output 000102037fc8ff 我设法想出
  • 模拟按键 X 秒

    这是我用来在某个进程中模拟 Tab 键按下的代码 DllImport user32 dll static extern bool PostMessage IntPtr hWnd UInt32 Msg int wParam int lPara
  • 如何确定生成的进程何时准备就绪? (使用 CreateProcess() 和 FindWindow())

    这应该很简单 我正在创建一个程序 该程序使用 win32 生成一个进程CreateProcess 功能 加载此进程后 我使用以下命令找到它的窗口FindWindow并使用它发送消息SendMessage 问题是 我如何知道该窗口何时准备好接
  • VSCode 终端建议不会自动完成

    VSCode 的 PowerShell 终端现在以灰色显示您可能想要输入的内容 大概来自历史记录 但似乎没有办法真正接受这个建议 按 Tab 键只是执行正常的 PowerShell 自动完成 通常是 cmdlet 或路径 这个功能是什么 我
  • Java 8 lambda 表达式身份契约

    The JavaDoc 为LambdaMetaFactoryJava 1 8 的指定 lambda 捕获 可能涉及新函数对象的分配 或者可能返回现有函数对象 但它没有指定何时以及在什么情况下它可能选择一种方式或另一种方式 看看实际执行情况L
  • Numpy 性能差异取决于数值

    在评估 Numpy 中的表达式时 我发现了奇怪的性能差异 我执行了以下代码 import numpy as np myarr np random uniform 1 1 1100 1100 进而 timeit np exp 0 5 myar
  • Dijkstra 负权重算法

    好吧 首先我知道 Dijkstra 不适用于负权重 我们可以使用 Bellman ford 代替它 但在我遇到的一个问题中 它指出所有边的权重都从 0 到 1 不包括 0 和 1 而路径的成本实际上就是产品 所以我的想法就是只取日志 现在所
  • Rails 6.1.5:未初始化常量 Mail::TestMailer

    为什么我在 Rails 6 1 5 Ruby 3 1 0 中遇到 未初始化常量 Mail TestMailer 错误 而在 Rails 6 1 5 Ruby 3 0 1 没有问题的情况下工作 发现以下来自Ruby 3 1 0 的新闻 The
  • 使用 PIL 翻译图像 [重复]

    这个问题在这里已经有答案了 如何使用 PIL 和 python 将图像在四个方向之一上平移 5 个像素 我已经看到我们可以使用im transform size AFFINE data 但我不知道该怎么做 Image transform s
  • 如何获得 Task.WhenAny 对 Task 和 CancellationToken 的效果?

    我有一个交互式任务 在 最糟糕 的情况下根本不会执行 因此它由TaskCompletionSource 我想等待此任务完成 或者我收到的令牌被取消 以先发生者为准 完成此类工作的完美工具是Task WhenAny 唯一的问题是它只需要任务