简体   繁体   English

在TryReceiveAll之后使用OutputAvailableAsync的BufferBlock死锁

[英]BufferBlock deadlock with OutputAvailableAsync after TryReceiveAll

While working on an answer to this question , I wrote this snippet: 在处理这个问题 的答案时 ,我写了这个片段:

var buffer = new BufferBlock<object>();
var producer = Task.Run(async () =>
{
    while (true)
    {
        await Task.Delay(TimeSpan.FromMilliseconds(100));
        buffer.Post(null);
        Console.WriteLine("Post " + buffer.Count);
    }
});
var consumer = Task.Run(async () =>
{
    while (await buffer.OutputAvailableAsync())
    {
        IList<object> items;
        buffer.TryReceiveAll(out items);
        Console.WriteLine("TryReceiveAll " + buffer.Count);
    }
});
await Task.WhenAll(consumer, producer);

The producer should post items to the buffer every 100 ms and the consumer should clear all items out of the buffer and asynchronously wait for more items to show up. 生产者应该每隔100毫秒将项目发布到缓冲区,并且消费者应该清除缓冲区中的所有项目并异步地等待更多项目显示。

What actually happens is that the producer clears all items once, and then never again moves beyond OutputAvailableAsync . 实际发生的是生产者清除所有项目一次,然后再也不会超出OutputAvailableAsync If I switch the consumer to remove items one by one it works as excepted: 如果我切换消费者逐个删除项目,它将作为例外工作:

while (await buffer.OutputAvailableAsync())
{
    object item;
    while (buffer.TryReceive(out item)) ;
}

Am I misunderstanding something? 我误会了什么吗? If not, what is the problem? 如果没有,问题是什么?

This is a bug in SourceCore being used internally by BufferBlock . 这是一个由BufferBlock内部使用的SourceCore中的错误。 Its TryReceiveAll method doesn't turn on the _enableOffering boolean data member while TryReceive does. TryReceiveAll方法不上转_enableOffering而布尔数据成员TryReceive一样。 That results in the task returned from OutputAvailableAsync never completing. 这导致从OutputAvailableAsync返回的任务永远不会完成。

Here's a minimal reproduce: 这是一个最小的重现:

var buffer = new BufferBlock<object>();
buffer.Post(null);

IList<object> items;
buffer.TryReceiveAll(out items);

var outputAvailableAsync = buffer.OutputAvailableAsync();
buffer.Post(null);

await outputAvailableAsync; // Never completes

I've just fixed it in the .Net core repository with this pull request . 我刚刚使用此拉取请求将其修复在.Net核心存储库中。 Hopefully the fix finds itself in the nuget package soon. 希望修复程序很快就会在nuget包中找到它。

Alas, it's the end of September 2015, and although i3arnon fixed the error it is not solved in the version that was released two days after the error was fixed: Microsoft TPL Dataflow version 4.5.24. 唉,它是2015年9月底,虽然i3arnon修复了错误,但在错误修复两天后发布的版本中没有解决:Microsoft TPL Dataflow版本4.5.24。

However IReceivableSourceBlock.TryReceive(...) works correctly. 但是,IReceivableSourceBlock.TryReceive(...)可以正常工作。 An extension method will solve the problem. 扩展方法将解决问题。 After a new release of TPL Dataflow it will be easy to change the extension method. 在新版本的TPL Dataflow之后,可以轻松更改扩展方法。

/// <summary>
/// This extension method returns all available items in the IReceivableSourceBlock
/// or an empty sequence if nothing is available. The functin does not wait.
/// </summary>
/// <typeparam name="T">The type of items stored in the IReceivableSourceBlock</typeparam>
/// <param name="buffer">the source where the items should be extracted from </param>
/// <returns>The IList with the received items. Empty if no items were available</returns>
public static IList<T> TryReceiveAllEx<T>(this IReceivableSourceBlock<T> buffer)
{
    /* Microsoft TPL Dataflow version 4.5.24 contains a bug in TryReceiveAll
     * Hence this function uses TryReceive until nothing is available anymore
     * */
    IList<T> receivedItems = new List<T>();
    T receivedItem = default(T);
    while (buffer.TryReceive<T>(out receivedItem))
    {
        receivedItems.Add(receivedItem);
    }
    return receivedItems;
}

usage: 用法:

while (await this.bufferBlock.OutputAvailableAsync())
{
    // some data available
    var receivedItems = this.bufferBlock.TryReceiveAllEx();
    if (receivedItems.Any())
    {
        ProcessReceivedItems(bufferBlock);
    }
}

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

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