Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么ProtocolReader忽略CancellationToken?_C#_Asp.net Core_.net Core_Kestrel Http Server - Fatal编程技术网

C# 为什么ProtocolReader忽略CancellationToken?

C# 为什么ProtocolReader忽略CancellationToken?,c#,asp.net-core,.net-core,kestrel-http-server,C#,Asp.net Core,.net Core,Kestrel Http Server,我的自定义ProtocolReader似乎忽略了CancellationToken,而且结果总是false 我的大部分代码来自: 我错过了什么 这是我的协议阅读器: public class CustomProtocolReader : IMessageReader<SocketObject> { public bool TryParseMessage(in ReadOnlySequence<byte> input, ref SequencePos

我的自定义
ProtocolReader
似乎忽略了
CancellationToken
,而且
结果总是
false

我的大部分代码来自:

我错过了什么

这是我的
协议阅读器

public class CustomProtocolReader : IMessageReader<SocketObject>
{
    public bool TryParseMessage(in ReadOnlySequence<byte> input,
        ref SequencePosition consumed, 
        ref SequencePosition examined, 
        out SocketObject message)
    {
        var reader = new SequenceReader<byte>(input);

        var payload = input.Slice(reader.Position, input.Length);
        message = new SocketObject { Buffer = payload.ToArray() };

        consumed = payload.End;
        examined = consumed;

        return true;
    }
}
while (true)
{
    try
    {
        var cts = new CancellationTokenSource();
        cts.CancelAfter(2000);

        // this should throw if nothing is received after 2 seconds.
        var result = await reader.ReadAsync(protocol, cts.Token); 

        if (result.IsCompleted || result.IsCanceled)  // this never hits
        {
            Console.WriteLine("Broke or cancelled");
            break;
        }
    }
    catch (OperationCanceledException) // this never gets fired
    {
        Console.WriteLine("Cancelled");
        break;
    }
    finally
    {
        reader.Advance();
    }
}
更新1:


默认的
connection.Transport.Input.ReadAsync
按预期工作,并遵守
CancellationToken

更新:Ohh well。。我只是忽略了。因此,是的,您应该期望在2秒钟后取消,甚至
ValueTask
也应该打印
IsCancled=true
:恐怕,但我不知道。我试图理解
Pipe/pipewaitable
的IValueTaskSource.on完整实现,但它非常复杂。我现在没有时间

由于历史原因,我将在这里给出答案

我的自定义ProtocolReader似乎忽略了CancellationToken

事实并非如此

首先,您必须知道,
CancellationToken
没有积极地执行某些操作。使用
CancellationToken.CancelAfter
时,它只会通知其来源
CancellationTokenSource
及其子项(由
CancellationTokenSource.CreateLinkedTokenSource
创建的取消令牌源),将其
IsCancellationRequested
设置为true,并触发所有处理程序(由
CancellationToken.Register
)在所有涉及的
CancellationTokenSource
中注册)。您可以通过不断查看
IsCancellationRequested
或通过向
CancellationToken.Register
注册处理程序来响应取消请求(请参见下面的示例实现)

调用
reader.ReadAsync
时最深的子例程是检查一次传递给
reader.ReadAsync
的令牌是否已被请求取消。让我详细说明一下:

我假设您的
读取器
类型是从(如中)创建的它在内部将
ConnectionContext的
Transport的
Input
传递给的构造函数。
ConnectionContext的实现应该是。属性Transport的类型为
IDuplexPipe
,在
SocketConnection.StartAsync
中创建,它创建并保存其属性y
Transport
SocketConnection.Transport
(请参见的属性
Transport
Transport
的类型有
Input
Output
,并且都是类型。此类在内部用于实现
System.IO.Pipelines.Pipe.ReadAsync
(请参阅类型的方法
ReadAsync
),方法是调用的
BeginOperation
。最后,此方法第一次调用您最初通过
var result=wait reader.ReadAsync(protocol,cts.token);
传递的令牌的
cancellationrequest()

因此,只有当您的取消没有被取消请求时,您的取消令牌才会被忽略

此外,结果“取消”总是错误的

您的结果不是任务。当您手动创建任务(Task.Factory.StartNew)时,您可以传递
CancellationToken
。当此令牌在任务创建之前或之后收到取消请求时,Task.IsCanceled将变为true,除非已经完成(请参阅)

但您可能已经认识到,您得到的不是Task,而是ValueTask。这是一个轻量级的任务实现,可以减少任务创建开销。它不接受任何
CancellationToken
。因此,您最初传递的
CancellationToken
不会用于通知正在取消
ValueTask
。换句话说,只要<代码>ValueTask
被创建,来自
CancellationTokenSource
的取消将不再将
ValueTask.IsCanceled
变为true。只有其内部保持变量以及Task或IValueTaskSource类型及其Task.IsCanceled或
IValueTaskSource.GetStatus
的实现可以更改此值,但这不是e情况(见下一段)

ValueTask接受任何“预先计算”的值,即实现IValueTaskSource的
任务
或“Task/”承诺。对于实现IValueTaskSource的值,指示
ValueTask.IsCanceled=true
的唯一方法是当
IValueTaskSource.GetStatus
返回
ValueTaskSourceStatus.Canceled
时。 因此,
Pipe
中的
ReadAsync
返回ValueTask包装的同步结果或ValueTask包装的仍然挂起/承诺的结果,这很重要,此挂起/承诺的结果将是一个类型为
Pipe
的内部变量,已在公共类
Pipe。但是您最初在
管道的上下文中传递的取消令牌。ReadAsync
未与
管道类型的内部变量进行交互(实现
IValueTaskSource
)以获取其实现方法
IValueTaskSource.GetStatus
以返回
ValueTaskSourceStatus.Cancelled

因此,您的任务永远不会成为真的原因是,在任何分支中,都不会创建带有初始取消令牌的
任务
,也不会实现与初始传递的取消令牌交互的
IValueTaskSource
实现

那么后果是什么?

当您的令牌被取消请求后调用ReadAsync时,您就没事了-您得到了所需的
操作CanceledException
。之后,您就不会受到预期取消行为的影响。因此,您必须从这里开始实施您的解决方案
        public Task ReadAsync() =>
            // This is just an example.
            Task.Delay(int.MaxValue);

        public async Task DoTaskNotForever(CancellationToken token = default)
        {
            using var cts = CancellationTokenSource.CreateLinkedTokenSource(token);
            cts.CancelAfter(2);
            var tcs = new TaskCompletionSource<object>();
            using var registration = cts.Token.Register(() => tcs.SetCanceled());

            while (true) {
                try {
                    await Task.WhenAny(ReadAsync(), tcs.Task).ConfigureAwait(false);
                } catch (OperationCanceledException error) { 
                    // Ensure to flush/close/reconnect socket !!! (see below why)
                }
            }
        }
using using Nito.AsyncEx;

...

while (true)
{
    try
    {
        var cts = new CancellationTokenSource();
        cts.CancelAfter(2000);

        // this should throw if nothing is received after 2 seconds.
        var result = await reader.ReadAsync(protocol, cts.Token).WaitAsync(cts.Token); 

        if (result.IsCompleted || result.IsCanceled)  // this should hit
        {
            Console.WriteLine("Broke or cancelled");
            break;
        }
    }
    catch (OperationCanceledException) // should get fired after 2 seconds unless ReadAsync has not yet finished
    {
        Console.WriteLine("Cancelled");
        break;
    }
    finally
    {
        reader.Advance();
    }
}