立即取消阻止操作超时

时间:2014-09-14 20:57:29

标签: c# .net multithreading async-await blocking

我有一个从队列中读取的阻塞操作,但可能需要超时。我可以轻松地将其转换为“异步”操作:

    public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
    {
        return await Task.Run(() =>
        {
            while (true)
            {
                cancellationToken.ThrowIfCancellationRequested();

                // Try receiving for one second
                IMessage message = consumer.Receive(TimeSpan.FromSeconds(1));

                if (message != null)
                {
                    return message;
                }
            }
        }, cancellationToken).ConfigureAwait(false);
    }

由于您可以泄漏资源,因此中止线程通常被认为是不好的做法,因此超时似乎是干净地停止线程的唯一方法。所以我有三个问题:

  1. “立即”取消的普遍接受的超时值是什么?
  2. 对于提供内置异步方法的库,是否真的存在立即取消,还是使用超时和循环来模拟它?也许这里的问题是如何使用软件中断,如果这些中断也需要进行某种轮询以检查是否存在中断,即使它是在内核/ CPU级别。
  3. 我是否有其他方法可以接近这个?
  4. 编辑:所以我可能已经使用Thread.Interrupt()找到了部分答案,然后处理ThreadInterruptedException。这基本上是内核级软件中断,并且尽可能接近“立即”吗?以下是更好的处理方法吗?

        public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
    
            var completionSource = new TaskCompletionSource<IMessage>();
    
            var receiverThread = new Thread(() =>
            {
                try
                {
                    completionSource.SetResult(consumer.Receive());
                }
                catch (ThreadInterruptedException)
                {
                    completionSource.SetCanceled();
                }
                catch (Exception ex)
                {
                    completionSource.SetException(ex);
                }
            });
    
            cancellationToken.Register(receiverThread.Interrupt);
    
            receiverThread.Name = "Queue Receive";
            receiverThread.Start();
    
            return await completionSource.Task.ConfigureAwait(false);
        }
    

1 个答案:

答案 0 :(得分:1)

  1. 这取决于您的具体需求。对于某些人来说,第二个可能是立竿见影的,而对其他人来说则是缓慢的
  2. 提供async API的库(好的)从下到上。它们通常不会使用线程包装阻塞(同步)操作,使它们看起来是异步的。他们使用TaskCompletionSource创建真正的async方法。
  3. 我不确定你的队列是什么意思(.Net中的内置Queue没有Receive方法)你可能应该使用真正的async TPL Dataflow {/ 1}}等BufferBlock数据结构

    关于您的特定代码示例。 你在整个操作过程中都是一个线程(那个async over sync),这个代价很高。您可以尝试快速使用,然后异步等待超时结束,或者CancellationToken被取消。 使用Task.Run的另一个线程也没有意义。您可以简单地将async lambda作为ReceiveAsync的内容:

    public async Task<IMessage> ReceiveAsync(CancellationToken cancellationToken)
    {
        while (true)
        {
            cancellationToken.ThrowIfCancellationRequested();
            // Try receiving for one second
            IMessage message;
            if (!consumer.TryReceive(out message))
            {
                 await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
            }
            if (message != null)
            {
                return message;
            }
        }
    }
    

    如果您的队列实施IDisposable,则取消Dispose时,另一个(更严厉)选项就是在其上调用CancellationTokenHere's how