BlockingCollection повторяет попытку, если его базовая коллекция не пуста, но TryTake терпит неудачу? - PullRequest
0 голосов
/ 22 марта 2020

BlockingCollection - это обертка вокруг IProducerConsumerCollection . Насколько я понимаю, вызов BlockingCollection.Take проверяет, есть ли доступный предмет (используя собственный счетчик). Затем он использует TryTake в своей базовой коллекции для извлечения указанного элемента.

Теперь, конечно, должен существовать элемент в базовой коллекции (и никаких внешних изменений не происходит). Но что произойдет, если TryTake завершится неудачей по какой-то другой причине, и базовая коллекция вернет false в TryTake? Оболочка просто сдается или пытается снова взять элемент (предположительно, в следующий раз)?

Зловещая «другая причина» может быть внутренней задержкой, например, если структура данных распределена.

Ответы [ 2 ]

2 голосов
/ 22 марта 2020

Ну, единственное, что вы можете принять как должное, это то, что написано в документации. Но вы можете просто взглянуть на исходный код :

public T Take()
{
    T item;

    if (!TryTake(out item, Timeout.Infinite, CancellationToken.None))
    {
        throw new InvalidOperationException(SR.GetString(SR.BlockingCollection_CantTakeWhenDone));
    }

    return item;
}

Другими словами, причина, по которой TryTake возвращает false, не имеет значения; Take бросит.

0 голосов
/ 29 марта 2020

@ Ответ Джои связан с исходным кодом и дал правильный результат, но проблема немного более тонкая.

BC.Take вызывает BC.TryTake ( не на базовом коллекция). BC.TryTake затем ждет семафор. Этот семафор увеличивается, когда элемент добавляется в B C.

. Затем он вызывает Underlying.TryTake и броски напрямую, если возвращается false . Сокращенно:

//If an item was successfully removed from the underlying collection.
removeSucceeded = m_collection.TryTake(out item);
if (!removeSucceeded) {
    // Check if the collection is empty which means that the collection was modified outside BlockingCollection
    throw new InvalidOperationException (SR.GetString(SR.BlockingCollection_Take_CollectionModified));
}

Это единственное место, где Underlying.TryTake вызывается в B C.

Так что TryTake действительно никогда не может потерпеть неудачу. Однако гарантируется (текущим кодом B C), что TryTake вызывается только после соответствующего TryAdd. Поэтому базовой структуре данных просто необходимо убедиться, что операции TryTake никогда не задерживаются после окончания операций TryAdd, поскольку B C не будет пытаться TryTake, пока TryAdd единственного элемент все еще выполняется .

...