First, short answers to your specific questions.
Does it throw if CompleteAdding was called in any collection? or both collections?
Both (all) - but only if there are no items available in any collection.
What if CompleteAdding was called and the collection still has some elements that it throws?
No. If the collection has an available item, it will be removed from the collection and returned to the caller.
Conclusion
The documentation seems to be unclear. Part
or CompleteAdding() is called in the collection
should have been worded differently - something like
whether or not there is an available item in any for collections, and CompleteAdding() was called in all collections
Justification
Well, I know that implementation is not good practice, but when the documentation is unclear, implementation is the only reliable and official source that I can think of. Therefore, using a reference source , both TakeFromAny and TryTakeFromAny call the private method TryTakeFromAnyCore . It starts with the following:
ValidateCollectionsArray(collections, false);
false here there is a bool argument called isAddOperation , and is used inside the ValidateCollectionsArray as follows:
if (isAddOperation && collections[i].IsAddingCompleted) { throw new ArgumentException( SR.GetString(SR.BlockingCollection_CantAddAnyWhenCompleted), "collections"); }
which is one of the possible places throwing an ArgumentException for collections with called CompleteAdding() . And, as we see, this is not so (question No. 1).
Then the implementation continues with the following “quick path”:
//try the fast path first for (int i = 0; i < collections.Length; i++) { // Check if the collection is not completed, and potentially has at least one element by checking the semaphore count if (!collections[i].IsCompleted && collections[i].m_occupiedNodes.CurrentCount > 0 && collections[i].TryTake(out item)) return i; }
This confirms the answer to question No. 2.
Finally, if there is no element available in any of the sets, the implementation takes a “slow path” by calling another private TryTakeFromAnyCoreSlow method, with the following comment TryTakeFromAnyCoreSlow a substantial explanation of the implemented behavior:
//Loop until one of these conditions is met: // 1- The operation is succeeded // 2- The timeout expired for try* versions // 3- The external token is cancelled, throw // 4- The operation is TryTake and all collections are marked as completed, return false // 5- The operation is Take and all collection are marked as completed, throw
The answer to both of our questions is in case No. 1 and case No. 5 (pay attention to the word all ). Btw, it also shows the only difference between TakeFromAny and TryTakeFromAny - case # 4 vs # 5, i.e. throw vs return -1 .