奇怪的Rx+CancellationToken问题:有时注册的回调未完成

2023-12-14

我观察到一个奇怪的现象,有时会在我编写的 Rx 查询中发生,其中涉及CancellationToken。两个回调注册到同一个CancellationToken,一个在查询之外,一个是查询的一部分。的意图CancellationToken是发出查询终止的信号。发生的情况是,有时第二个回调卡在执行中间,永远不会完成,从而阻止调用第一个回调。

下面是重现该问题的最小示例。虽然不是很小,但我不能再减少了。例如更换Switch运算符与Merge使问题消失。如果抛出异常,也会发生同样的情况Task.Delay(1000, cts.Token)被吞掉了。

public class Program
{
    public static void Main()
    {
        var cts = new CancellationTokenSource(500);
        cts.Token.Register(() => Console.WriteLine("### Token Canceled! ###"));
        try
        {
            Observable
                .Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(1000))
                .TakeUntil(Observable.Create<Unit>(observer =>
                    cts.Token.Register(() =>
                    {
                        Console.WriteLine("Before observer.OnNext");
                        observer.OnNext(Unit.Default);
                        Console.WriteLine("After observer.OnNext");
                    })))
                .Select(_ =>
                {
                    return Observable.StartAsync(async () =>
                    {
                        Console.WriteLine("Action starting");
                        await Task.Delay(1000, cts.Token);
                        return 1;
                    });
                })
                .Switch()
                .Wait();
        }
        catch (Exception ex) { Console.WriteLine("Failed: {0}", ex.Message); }
        Thread.Sleep(500);
        Console.WriteLine("Finished");
    }
}

预期输出:

Action starting
Before observer.OnNext
After observer.OnNext
### Token Canceled! ###
Failed: A task was canceled.
Finished

实际输出(有时):

Action starting
Before observer.OnNext
Failed: A task was canceled.
Finished

在小提琴上尝试一下。您可能需要运行该程序 3-4 次才能出现问题。请注意两个缺失的日志条目。看来是调用observer.OnNext(Unit.Default);永远不会完成。

我的问题是:有谁知道导致这个问题的原因是什么?另外,我该如何修改CancellationToken- 查询的相关部分,以便它执行其预期目的(终止查询),而不会干扰相同的其他注册回调CancellationToken?

.NET 5.0.1 和 .NET Framework 4.8、System.Reactive 5.0.0、C# 9

Update:还有 .NET 6.0 和 System.Reactive 5.0.0 (截屏拍摄于 2022 年 6 月 4 日)


还有一项观察:如果我修改以下内容,问题就会停止出现Observable.Create委托,以便它返回一个Disposable.Empty代替CancellationTokenRegistration, 像这样:

.TakeUntil(Observable.Create<Unit>(observer =>
{
    cts.Token.Register(() =>
    {
        Console.WriteLine("Before observer.OnNext");
        observer.OnNext(default);
        Console.WriteLine("After observer.OnNext");
    });
    return Disposable.Empty;
}))

但我不认为忽略由返回的注册cts.Token.Register是一个修复。


一年后,我无法重现它。你还可以吗?我已经在 .NET Framework 4.8 和 .NET 6 上尝试过。

然而,在我的相关问题,我刚刚处理了导致死锁的竞争条件,这might与您所描述的问题相关。

CancellationTokenRegistration.Dispose本质上是一个阻塞调用,它等待所有回调完成,并且这些回调在调用的线程上执行CancellationTokenSource.Cancel(例如,随机计时器回调线程),这可能与调用的线程不同DisposeRx 订阅的数量。

我还没有深入挖掘,但我的案例的解决方法是不调用令牌注册Dispose如果令牌已经被取消。这应该没问题,因为在发出取消信号后令牌注册将被丢弃,并且当 Rx 调用时我们不再对取消回调感兴趣Dispose:

    .TakeUntil(Observable.Create<Unit>(observer => 
    {
        var token = cts.Token;
        var rego = token.Register(() =>
        {
            Console.WriteLine("Before observer.OnNext");
            observer.OnNext(Unit.Default);
            Console.WriteLine("After observer.OnNext");
        });
        return Disposable.Create(() => 
        {
            if (!token.IsCancellationRequested)
            {
                // this 
                rego.Dispose();
            }
        });
    }))       

在 .NET 6 中,另一possible解决方法可能是使用CancellationTokenRegistration.DisposeAsync(尽管我还没有验证它是否可以解决我的情况下的僵局):

        return Disposable.Create(() => 
        {
            DisposeAsync();
            async void DisposeAsync() => 
              await rego.DisposeAsync().ConfigureAwait(false);
        });

Updated,正如 Theodor 在评论中提到的,使用CancellationTokenRegistration.Unregister代替Dispose是另一种解决方法(非阻塞),这对我来说感觉更干净。

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

奇怪的Rx+CancellationToken问题:有时注册的回调未完成 的相关文章

  • 如何以编程方式将访问键(快捷方式)添加到 WPF ContextMenu?

    我已经有以下内容 var myContextMenu new System Windows Controls ContextMenu var exitItem new MenuItem exitItem Header E xit exitI
  • 如何在C++中生成非常大的随机数

    我想使用 C 生成 0 2 64 范围内的非常大的随机数 我已经使用了 rand 函数 但它没有生成非常大的数字 有人可以帮忙吗 使用c 11 使用标准c 11的随机库 http en cppreference com w cpp nume
  • 如何通过覆盖 MSBuild 目标来防止外语资源生成?

    我正在致力于减少大型 C ASP NET 解决方案的编译时间 我们的解决方案使用通常的 resx 文件方法翻译成大约十几种外语 这些资源文件的解析和编译极大地减慢了我们的编译时间 并且是日常的挫败感 我知道可以创建自定义资源提供程序并摆脱
  • C# 异步任务比同步慢

    你知道为什么同步斐波那契方法比异步 等待更快并且比异步任务更快吗 我在每个项目方法上都使用了异步 所以主要是这是一个非常糟糕的方法 Code static int FibonacciSync int number if number 0 r
  • 信号与信号2

    我的应用程序可能会受益于使用 boost 的信号库之一而不是本土解决方案 该应用程序是多线程的 但执行信号处理的部分是单线程的 如果多线程不是问题 是否有任何理由更喜欢 Boost Signals2 而不是 Boost Signal Boo
  • 头文件中实现的函数的静态与内联

    我想到的方式inline在 C 中用于链接 作用域 我把它放在同一个篮子里extern and static对于全局对象 通常 对于在头文件中实现的函数 我的首选解决方案是将其设为静态 In Foo h static void foo Do
  • C# 中 PKCS11Interop 库的线程安全使用 [已关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在使用 PKCS11Interop 在 HSM 内执行密钥管理操作 我使用的 HSM 是 Thales PCI Express 下面是
  • 预编译头和 Visual Studio

    有没有办法设置 Visual Studio 解决方案参数 以便它只创建预编译头而不构建整个解决方案 具体来说 它是一个巨大的 C 解决方案 本身有许多项目 谢谢 仅选择 pch 创建者源文件 通常是 stdafx cpp 然后编译该文件 C
  • 如何从不同的线程访问控件?

    如何从创建控件的线程以外的线程访问控件 避免跨线程错误 这是我的示例代码 private void Form1 Load object sender EventArgs e Thread t new Thread foo t Start p
  • 无法将参数从 `const char *` 转换为 `char *`

    鉴于此代码 void group build int size std string ips Build the LL after receiving the member list from bootstrap head new memb
  • 节点*链表中的下一个

    我是数据结构和算法的新手 我遇到了以下代码 typedef struct node int data node next 谁能告诉我为什么我们要声明节点 next next 不能声明为 int next 吗 因为你希望能够做到n gt ne
  • 如果finally 块包含await,为什么*有时*不会在ThreadAbortException 上执行?

    UPDATE 我不认为这个问题是重复的ThreadAbortException最后可以跳过吗 https stackoverflow com questions 18002668 can threadabortexception skip
  • 使用 dateTimePicker 在 DataGridView 中编辑日期

    我有一个DateTime我的 WinForms 中的专栏DataGridView 目前只能通过手动输入日期来编辑该字段 例如 2010 09 02 需要什么才能拥有一个DateTimePicker 或同等 用作编辑器 DataGridVie
  • 在 C# .NET 中对非 ASCII 字符进行编码

    我想向我的应用程序发送的电子邮件添加自定义标头 标头名称只能包含 ASCII 字符 但对于值和用户可能会输入 UTF 8 字符 我必须对它们进行 Base64 编码 此外 我还必须将它们解码回 UTF 8 以便在 UI 中向用户显示它们 最
  • 应在堆栈上分配的最大数量

    我一直在寻找堆栈溢出有关应在堆栈上分配的最大内存量的指南 我看到了堆栈与堆分配的最佳实践 但没有关于应该在堆栈上分配多少以及应该在堆上分配多少的指南 有什么想法 数字可以作为指导吗 什么时候应该在堆栈上分配 什么时候应该在堆上分配 多少才算
  • 实体框架读取列但阻止其更新

    给定一个数据库表 其中有一列包含历史数据但不再填充 实体框架中是否有一种方法可以读取该列 但在使用相同的模型对象时防止它被更新 例如我有一个对象 public class MyObject public string CurrentData
  • 为什么 C++ 标准没有将 sizeof(bool) 定义为 1?

    Size of char signed char and unsigned char由 C 标准本身定义为 1 个字节 我想知道为什么它没有定义sizeof bool also C 03 标准 5 3 3 1 说 sizeof char s
  • Asp.Net Core 中的 SSL 不起作用

    我从 Visual Studio 创建了一个简单的 Web 应用程序Web Application Net Core 具有个人用户帐户授权的模板 然后 我启用了 SSLProject gt MyProject Properties 将带有
  • 在 C# 中使用自定义千位分隔符

    在显示字符串时 我尝试不使用 字符作为千位分隔符 而是使用空格 我想我需要定义一种自定义文化 但我似乎做得不对 有什么指点吗 例如 将 1000000 显示为 1 000 000 而不是 1 000 000 no String Replac
  • 如何根据当前日期时间发现财政年度?

    我需要基于当前或今天的日期时间的财政年度 假设我们认为今天的日期是10 April 2011 那么我需要输出为Financial Year 2012在某些情况下 我需要以短格式显示相同的输出FY12 我想以两种方式显示 在我们的要求中 考虑

随机推荐

  • Git - 在线存储库中有未跟踪的文件[重复]

    这个问题在这里已经有答案了 我正在使用 git bitbucket 来控制我的 Linux 配置文件 所有文件都在目录中 cfg 然后我另外还有一些本地配置文件 cfg local 这些应该因机器而异 我想在我的在线存储库中保留本地文件的副
  • Java - for循环终止表达式之间的区别

    我只是好奇 这两个循环实现之间的速度和性能是否有差异 假使 假设size 方法返回处理一组元素的数组 集合或对象的长度 实际上它来自XOM api 实施1 int size someArray size for int i 0 i lt s
  • 如何在 OPENROWSET(BULK...) 中动态指定文件的路径?

    我想将图像插入图像字段 最好使用接受图像路径的存储过程 经过一番折腾后我想出了这个 functional DECLARE parameters nvarchar max DECLARE sql string nvarchar max N U
  • 如何从 ILNumerics 曲面图上鼠标的单击位置找到曲面的 3D 坐标?

    目前 我们的系统使用 ILNumerics 3D 绘图立方体类和 ILNumerics 曲面组件来显示 3D 网格曲面 我们系统的目标是能够通过鼠标单击绘图来询问表面上的各个点 我们在绘图上设置了 MouseClick 事件 问题是我不确定
  • 如何从 R 中的 nls 获取绘图?

    在 R 中 我使用 nls 进行非线性最小二乘拟合 那么如何使用拟合提供的系数值绘制模型函数呢 是的 这是一个very来自 R 相关新手的天真问题 使用第一个例子 nls按照我逐行指出的示例 可以实现以下目标 This is just ou
  • 在 Objective-C 中检查空字符串的正确方法是什么?

    我在我的 iPhone 应用程序中使用了这个 if title nil do something 但它引发了一些异常 并且控制台显示标题为 null 所以我现在用这个 if title nil title isKindOfClass NSN
  • 如何在 Pandas 数据框中展开列

    我有以下熊猫数据框 import pandas as pd import numpy as np df pd DataFrame fc 100 100 112 1 3 14 125 sample id S1 S1 S1 S2 S2 S2 g
  • VC++ 增量链接器错误 LNK1000

    免责声明 我是一名 C 人员 几乎没有 VS C MFC 经验 我正在尝试在我的机器上构建一些混合 C Net 的项目 当在我的机器上通过命令行编译 C 项目时 我收到此错误 链接 致命错误 LNK1000 IMAGE BuildImage
  • 等待谷歌服务器端函数解析的最简单方法

    我需要客户端代码等待被调用的服务器端 google script run 函数完成 然后再运行更多代码 The withSuccessHandler successFunc 不会导致服务器调用之后的代码行等待 我做了什么 async fun
  • JavaBeans 属性适配器如何工作?

    如果我遵循所描述的 JavaFX 属性定义 我想要做的事情就很好here 现在 我想使用 Java Beans 属性适配器来定义 Java Beans 对象的属性 由于没有文档 我无法弄清楚它是如何工作的 假设我有一个简单的 POJO 类
  • 将整数作为常量引用传递与复制

    这可能是一个愚蠢的问题 但我注意到在大量 API 中 许多采用不打算修改的整数参数的方法签名如下所示 void method int x 而不是 void method const int x 对我来说 这两个似乎都会function一模一
  • aws-sdk: NoSuchKey: 指定的密钥不存在?

    在我的nodejs项目中 我使用aws sdk从我的s3存储桶下载所有图像 但我收到此错误 NoSuchKey 指定的密钥不存在 但密钥是正确的 我可以使用这些密钥上传图像 我的代码是 var AWS require aws sdk s3
  • 将计划的开始值和结束值建模为变量

    我正在尝试在 Anylogic 中构建一个模型 该模型将优化资源池的开始和结束时间以满足需求 有没有办法将计划的开始时间和结束时间作为参数输入到优化模型中 我目前的班次是8 5 可以满足30天的需求 但优化模型可能会说6 6 我们加班 可以
  • 什么是 /sys/class/gpio/export 和 `/sys/class/gpio/unexport 机制以及底层 sysfs 功能是什么?

    在 Android 和 Linux 下使用旧版 sysfs GPIO 的第一步是export您要使用的特定 GPIO 引脚 当您完成 GPIO 引脚的操作后unexport it 我一直在寻找一个解释export命令实际上是这样做的 但是我
  • 如何在 DataTable 中显示加载/处理消息?

    在我的应用程序中我正在使用数据表网 var ticketHistoryDataTable ticketHistoryData DataTable paging false data searching false columns data
  • Highcharts饼图可以有url链接

    我在我的应用程序中使用 Highcharts 饼图 其中饼图的数据是从数据库填充的 我的问题是填充饼图后 如果我单击某个区域 它应该呈现到特定的 php 页面 是否可以 这是我的代码 function open risk level pie
  • Angular2 - API 接收的内容中的动态组件

    我有一个组件 该组件的内容由 API 接收 并且它包含另一个组件 问题是 如何渲染子组件 当我将接收到的内容放入innerHTML 中时 组件标签将被删除 我检查了所有有关使用resolveComponentFactory创建组件的文章 但
  • 移动 JS 应用程序上的 Location.reload()

    在桌面上 您可以使用location reload 它将重新加载页面 重新启动页面上的代码 但是如果我想 重新加载 一个 JavaScript 应用程序怎么办 有办法这样做吗 谢谢 location reload Page reloads
  • Android EditText如何在所有字符的末尾启动光标指针 - 而不是第一行的末尾

    我有使用 EditText 的评论框 它有几行 当用户返回到该框时 如果框中总共有三行 他们总是位于第 1 行的末尾 而不是第 3 行的末尾 我怎样才能解决这个问题 也许有更简单的方法 但一种方法是使用 editText setSelect
  • 奇怪的Rx+CancellationToken问题:有时注册的回调未完成

    我观察到一个奇怪的现象 有时会在我编写的 Rx 查询中发生 其中涉及CancellationToken 两个回调注册到同一个CancellationToken 一个在查询之外 一个是查询的一部分 的意图CancellationToken是发