BufferBlock deadlock with OutputAvailableAsync after TryReceiveAll

前端 未结 2 1800
闹比i
闹比i 2021-01-31 18:29

While working on an answer to this question, I wrote this snippet:

var buffer = new BufferBlock();
var producer = Task.Run(async () =>
{
    whi         


        
                      
相关标签:
2条回答
  • 2021-01-31 19:18

    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.

    However IReceivableSourceBlock.TryReceive(...) works correctly. An extension method will solve the problem. After a new release of TPL Dataflow it will be easy to change the extension method.

    /// <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);
        }
    }
    
    0 讨论(0)
  • 2021-01-31 19:31

    This is a bug in SourceCore being used internally by BufferBlock. Its TryReceiveAll method doesn't turn on the _enableOffering boolean data member while TryReceive does. That results in the task returned from OutputAvailableAsync never completing.

    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. Hopefully the fix finds itself in the nuget package soon.

    0 讨论(0)
提交回复
热议问题