Ask a questionAsk a question
 

AnswerWhen BlockingCollection<T>.TakeFromAny() return -1?

  • Tuesday, October 20, 2009 4:03 PMVitaly DilmukhametovMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    Hi2all!

    I have one question: when BlockingCollection<(Of <T>)>.TakeFromAny() static method return -1?

    I read MSDN Docs that TakeFromAny() returns:
    The index of the collection in the collections array from which the item was removed, or -1 if an item could not be removed.

    Ok, but when item couldn't be removed? When collections parameter array contains empty not completed BlockingCollection<T> instances, the thread, called TakeFromAny() is blocked.

    Let me to show you an example. I have console application:
    using System;
    using System.Collections.Generic;
    //using System.Linq;
    using System.Text;
    using System.Collections.Concurrent;
    using System.Threading;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                BlockingCollection<int> col1 = new BlockingCollection<int>();
                BlockingCollection<int> col2 = new BlockingCollection<int>();
    
    
                //Timer t1 = new Timer(delegate
                //    {
                //       // col1.Add(111);
                //        col1.CompleteAdding();
    
                //    }, null, 1000, Timeout.Infinite);
                //Timer t2 = new Timer(delegate
                //    {
                //        //col2.Add(222);
                //        //Thread.SpinWait(30000000);
                //        col2.CompleteAdding();
                        
    
                //    }, null, 2000, Timeout.Infinite);
    
                Console.WriteLine(DateTime.Now.ToLongTimeString());
                
                int value;
                int indexOfCollection = BlockingCollection<int>.TakeFromAny(new[] { col1, col2 }, out value);
                Console.WriteLine(DateTime.Now.ToLongTimeString() + " " + value + " index = "+indexOfCollection);
    
                Console.WriteLine("Done");
                Console.ReadLine();
            }
        }
    }
    

    In this case main app thread is blocked forever. If I uncomment lines with 2 timers creation, I have ArgumentException : At least one of the specified collections is marked as complete with regards to additions.
    Parameter name: collections

    If I uncomment additional lines, where call Add method to both collections - it's ok, I take 111 value from col1.

    So I can't imagine situation, where TryTakeFrom() returns -1. Can you explain me, when it's possible?

    Thanks in advance!

    P.S. My Env: windows 2008 R2 RC, VS 2010 beta 2.

Answers

All Replies

  • Tuesday, October 20, 2009 4:32 PMReed Copsey, Jr. Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    TakeFromAny() and TryTakeFromAny() will return -1 if the collections are all in a valid state, but are all empty (they've had CompleteAdding() called, but have already had all of their elements removed).

    Once the collections have had CompleteAdding() called, they no longer block on remove attempts if the collection is empty.  If all of the collections are this way, your "Take.." methods will not block, but not succeed, and will return -1.

    Typically, this happens when you're task(s) are complete, since you've processed all of the items in all of the collections...
    Reed Copsey, Jr. - http://reedcopsey.com
  • Tuesday, October 20, 2009 5:19 PMVitaly DilmukhametovMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    Ok, I have 2 collections, which are empty and "completed", and I call TakeFromAny:
    using System;
    using System.Collections.Generic;
    //using System.Linq;
    using System.Text;
    using System.Collections.Concurrent;
    
    namespace ConsoleApplication67
    {
        class Program
        {
            static void Main(string[] args)
            {
                BlockingCollection<int> col1 = new BlockingCollection<int>();
                BlockingCollection<int> col2 = new BlockingCollection<int>();
    
                //col1.Add(1);
                col1.CompleteAdding();
                col2.CompleteAdding();
    
                int val = 0;
                Console.WriteLine(BlockingCollection<int>.TakeFromAny(new[] { col1, col2 }, out val));
    
                Console.WriteLine("Done");
                Console.ReadLine();
            }
        }
    }
    
    And I have the same ArgumentException with the same message.
    When I uncomment col1.Add(1); line, I succesfully take element 1 from col1.

    So, in this case TakeAnyFrom also don't return -1.

    If I understand you incorrectly, please let me know. Thank you for reply!
  • Tuesday, October 20, 2009 9:09 PMReed Copsey, Jr. Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    I apologize - and I see your point.  I don't believe TakeAnyFrom, from what I can see, will ever return -1.

    I was thinking of TryTakeFromAny, which does return -1.  For example:

    namespace ConsoleApplication1
    {
        using System;
        using System.Collections.Concurrent;
    
        class Program
        {
            static void Main(string[] args)
            {
                BlockingCollection<int> col1 = new BlockingCollection<int>();
                BlockingCollection<int> col2 = new BlockingCollection<int>();
                int val = 0;
                Console.WriteLine(BlockingCollection<int>.TryTakeFromAny(new[] { col1, col2 }, out val));
                Console.WriteLine("Done");
                Console.ReadLine();
            }
        }
    }
    
    

    This prints -1 immediately.
    Reed Copsey, Jr. - http://reedcopsey.com
  • Tuesday, October 20, 2009 9:20 PMReed Copsey, Jr. Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    I filed Feedback on Connect for this issue.  I believe it is just a documentation issue, as TryTakeFromAny provides the -1 behavior, and TakeFromAny's behavior seems reasonable.
    Reed Copsey, Jr. - http://reedcopsey.com
  • Wednesday, October 21, 2009 1:27 AMVitaly DilmukhametovMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Great! Thank you for support.

    I think it's "copy-paste" documentation issue too. And TakeFromAny() doesn't return -1, because it return index on success, generate an exception in some cases, or wait for element continiously.
  • Thursday, October 29, 2009 7:02 PMJosh PhillipsMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Great find Vitaly! 

    I've filed a documentation bug to get this corrected.  This includes removing the reference to -1 and documenting the exception that might be thrown if you pass a BlockingCollection<T> that has had CompletedAdding called on it and been passed to TakeFromAny.

    Josh