Некоторое время в моей компании мы использовали домашнюю версию ObjectPool<T>
, которая обеспечивает блокировку доступа к ее содержимому. Это довольно просто: a Queue<T>
, a object
для блокировки и AutoResetEvent
для подачи потока "заимствования" при добавлении элемента.
Мясо класса действительно эти два метода:
public T Borrow() {
lock (_queueLock) {
if (_queue.Count > 0)
return _queue.Dequeue();
}
_objectAvailableEvent.WaitOne();
return Borrow();
}
public void Return(T obj) {
lock (_queueLock) {
_queue.Enqueue(obj);
}
_objectAvailableEvent.Set();
}
Мы использовали этот и несколько других классов коллекций вместо тех, которые были предоставлены System.Collections.Concurrent
, потому что мы используем .NET 3.5, а не 4.0. Но недавно мы обнаружили, что, поскольку мы используем Reactive Extensions, у нас фактически есть пространство имен Concurrent
, доступное нам (в System.Threading.dll).
Естественно, я понял, что поскольку BlockingCollection<T>
является одним из основных классов в пространстве имен Concurrent
, он, вероятно, будет предлагать лучшую производительность чем что-либо, что написал я или мои товарищи по команде.
Поэтому я попытался написать новую реализацию, которая работает очень просто:
public T Borrow() {
return _blockingCollection.Take();
}
public void Return(T obj) {
_blockingCollection.Add(obj);
}
К моему удивлению, согласно некоторым простым тестам (заимствование/возврат в пул несколько тысяч раз из нескольких потоков), наша первоначальная реализация значительно превосходит BlockingCollection<T>
с точки зрения производительности. Они оба работают правильно; это просто то, что наша первоначальная реализация кажется намного быстрее.
Мой вопрос:
- Зачем это было? Возможно ли это потому, что
BlockingCollection<T>
обеспечивает большую гибкость (я понимаю, что это работает, обертываяIProducerConsumerCollection<T>
), что обязательно приводит к издержкам производительности? - Это просто ошибочное использование класса
BlockingCollection<T>
? - Если это подходящее использование
BlockingCollection<T>
, я просто не правильно использую? Например, подходTake
/Add
слишком упрощен, и существует гораздо более эффективный способ получить одну и ту же функциональность?
Если у кого-то есть какое-то понимание в ответ на этот третий вопрос, похоже, что мы будем придерживаться нашей первоначальной реализации на данный момент.