Некоторое время в моей компании мы использовали домашнюю версию 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слишком упрощен, и существует гораздо более эффективный способ получить одну и ту же функциональность?
Если у кого-то есть какое-то понимание в ответ на этот третий вопрос, похоже, что мы будем придерживаться нашей первоначальной реализации на данный момент.