繁体   English   中英

BufferBlock.ReceiveAsync(timeout) 挂起,但 BufferBlock.ReceiveAsync() 工作正常

[英]BufferBlock.ReceiveAsync(timeout) hang but BufferBlock.ReceiveAsync() works OK

要么我做错了什么,但是尽管指定了 1 秒的超时,但下面永远不会返回它永远挂在ReceiveAsync上。

我希望它在超时后返回 null 值。

/* snipped MyContainer class */

private readonly BufferBlock<byte[]> queue = new BufferBlock<byte[]>();

public async Task ExecuteAsync(CancellationToken stoppingToken)
{
     // makes no difference if creating with TaskCreationOptions.LongRunning
     await Task
            .Factory
            .StartNew(async () =>
            {

              while (stoppingToken.IsCancellationRequested == false)
              {                     
                 // we get here OK, but no further if i use TimeSpan for delay
                 // without Timespan i.e. ReceiveAsync() only, it does **not** hang
                 var item = await 
                             this
                               .queue
                               .ReceiveAsync(TimeSpan.FromMilliseconds(1000));

                      // process it, but we never get here we sleep forever
                      await ProcessAsync(item);
                    }
             } /*,TaskCreationOptions.LongRunning*/);

     // we get here and print the below OK
     Console.WriteLine("thread created and running");
}

// this is called by the original (or some other) thread
// either if i call this or not, the above thread code still locks on ReceiveAsync
public void Add(byte[] data)
{
   Console.WriteLine("adding");
   this.queue.Post(data);
   Console.WriteLine("done"); // gets posted OK     
}

重要更新 - 如果我不指定延迟,则可以正常工作

var item = await this.queue.ReceiveAsync());

如果我消除延迟,代码可以正常工作,但是我每秒都会做一些后台管理(对于数据包计数器等),所以如果在 1 秒内没有收到任何内容,这对于唤醒很重要。

其他注意事项:

我从一个通用的 dot net worker 主机调用上面的代码:

public class Worker : BackgroundService
{
    private readonly MyContainer containerClass;
    private readonly ILogger<Worker> logger;

    public Worker(MyContainer containerClass, ILogger<Worker> logger)
    {
        this.containerClass = containerClass;
        this.logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        this.containerClass.ExecuteAsync(stoppingToken);

        while (!stoppingToken.IsCancellationRequested)
        {                
            this.logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

上面是在IHostBuilder构建worker之后调用的,我调用Host.Run()

我的理解是(我显然需要继续努力),因为我创建了线程。 它应该完全独立于(而不是阻塞)创建/调用它的线程运行......换句话说,它应该能够在线程本身内调用 ReceiveAsync 而不会被阻塞。

Task.Factory.StartNew与异步委托一起使用会创建一个嵌套任务:

Task<Task> nestedTask = Task.Factory.StartNew(async () => { //...

您正在等待外部任务,而不是内部任务,因此内部任务变成了即发即弃的任务。 通过使用await运算符两次,可以在一行中等待两个任务:

await await Task.Factory.StartNew(async () => { //...

或者,您可以使用Unwrap方法将这两个任务合二为一。

await Task.Factory.StartNew(async () => { /* ... */ }).Unwrap();

...或者甚至更好地使用Task.Run方法而不是Task.Factory.StartNew ,因为前者理解异步委托,并为您解包:

await Task.Run(async () => { //...

如果您对Task.Factory.StartNewTask.Run之间的区别感兴趣,可以在此处阅读内容丰富的文章。

感谢所有回复并最终回复 Enrico 的人(请随意复制/粘贴,我会将答案分配给您),代码实际上运行正常。

引发了 TimeoutException 异常,但没有被我的代码或 Visual Studio 捕获。

根据https://docs.microsoft.com/en-us/visualstudio/debugger/managing-exceptions-with-the-debugger?view=vs-2019启用所有 CLR 异常,异常开始被抛出。

然后我在代码中处理了异常,并且能够按照我的设计要求进行:

public Task ExecuteAsync(CancellationToken stoppingToken)
{
    return Task
            .Factory
            .StartNew(async () => {

                while (stoppingToken.IsCancellationRequested == false)
                {
                    try
                    {
                        var ts = TimeSpan.FromSeconds(UpdateFrequencySeconds);
                        var item = await this.queue.ReceiveAsync(ts);
                        await ProcessAsync(item);
                    }
                    catch (TimeoutException)
                    {
                        // this is ok, timer expired 
                    }
                    catch (Exception e)
                    {
                        this.logger.LogError(e.ToString());
                    }

                    UpdateCounters();
                }

                await StopAsync();
              },
              stoppingToken,
              TaskCreationOptions.LongRunning, 
              TaskScheduler.Default)
              .Unwrap();
 }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM