c# - 是否可以将 `ref bool` 转换为 CancellationToken?

标签 c# .net async-await task-parallel-library cancellation

我有一个遗留场景,其中使用 ref bool 向实现发送取消信号。现在,我想调用一个基于 Task 的库方法,该方法采用 CancellationToken 实例,我也希望在 bool 值更改时取消该实例。

这就是我必须处理的事情:

void Method(ref bool isCancelled)
{
    while (!isCancelled)
    {
        ...
        DoThis();
        DoThat();
        ...
    }
}

这就是我想做的:

Task MethodAsync(ref bool isCancelled)
{
    while (!isCancelled)
    {
        ...
        DoThis();
        await DoTheNewThingAsync(isCancelled.ToCancellationToken());
        DoThat();
        ...
    }
}

ToCancellationToken() 当然在此上下文中不存在,仅用于显示意图。

我尝试创建 CancellationTokenSource 的自定义实现,但该类中没有任何可以使用的虚拟内容。也不可能直接创建自定义 CancellationToken,因为它是一个 struct 并且无法继承。

我知道使用 ref bool 是一种糟糕的做法,但我目前无法更改依赖它的底层实现,因此我需要一种方法来使用它的值作为取消基于任务的调用机制。

最佳答案

这很复杂。出于以下几个原因:

  1. 您无法通过 ref 将参数传递给 async 方法。您正在使用 await,但要使用 await,您的方法需要标记为 async。并且async方法不能有ref参数。例如,这不会编译:
async Task MethodAsync(ref bool isCancelled)
{
    while (!isCancelled)
    {
        DoThis();
        await DoTheNewThingAsync(isCancelled.ToCancellationToken());
        DoThat();
    }
}

这会给你编译器错误:

CS1988: Async methods cannot have ref, in or out parameters

  • 您不能在匿名方法中使用 ref 参数。我考虑过使用 Timer 来检查变量。像这样的事情:
  • public static CancellationToken ToCancellationToken(ref bool isCancelled)
    {
        var tokenSource = new CancellationTokenSource();
    
        var timer = new System.Timers.Timer()
        {
            AutoReset = true,
            Interval = 100
        };
        timer.Elapsed += (source, e) =>
        {
            if (isCancelled)
            {
                tokenSource.Cancel();
                timer.Dispose();
            }
        };
        timer.Enabled = true;
    
        return tokenSource.Token;
    }
    

    但这会给你编译器错误:

    CS1628: Cannot use ref, out, or in parameter 'isCancelled' inside an anonymous method, lambda expression, query expression, or local function

    我没有看到任何其他方法可以通过引用将 bool 放入事件处理程序。

  • 我能得到的最接近的是这样的:
  • void Method(ref bool isCancelled)
    {
        while (!isCancelled)
        {
            DoThis();
            using (var tokenSource = new CancellationTokenSource()) {
                var mytask = DoTheNewThingAsync(tokenSource.Token);
                while (true)
                {
                    //wait for either the task to finish, or 100ms
                    if (Task.WaitAny(mytask, Task.Delay(100)) == 0)
                    {
                        break; //mytask finished
                    }
                    if (isCancelled) tokenSource.Cancel();
                }
    
                // This will throw an exception if an exception happened in
                // DoTheNewThingAsync. Otherwise we'd never know if it
                // completed successfully or not.
                mytask.GetAwaiter().GetResult();
            }
            DoThat();
        }
    }
    

    但是,这会阻止调用者,所以我不完全明白这有什么用(如果被阻止,调用者如何更改 isCancelled?)。但这就是您现有方法正在做的事情,所以也许它会起作用?

    但这太hacky了。如果您完全可以控制上游如何完成任何事情,那就这样做。

    关于c# - 是否可以将 `ref bool` 转换为 CancellationToken?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58661375/

    相关文章:

    c# - C#中字典的使用

    c# - 我们不能在 PreviewKeyDown 事件中获取文本框的当前值吗?

    javascript - C#:基于 .NET 的网络浏览器不显示图像

    c# - 如何设置 DataGridColumn 的宽度以适应内容 ("Auto"),但完全填充 MVVM 中 DataGrid 的可用空间?

    c# - 解码/解密期间 Base-64 字符数组的长度无效

    c# - 正确使用 return Task.FromException

    .net - 我应该仅出于记录目的而重写 ToString 吗?

    c# - 在 Windows Phone 中获取位置时出错

    asynchronous - Blazor InvokeAsync 与 await InvokeAsync

    javascript - 导出的异步函数的 Typescript 调用签名错误