У кого-нибудь есть хороший ресурс по реализации стратегии общего пула объектов для ограниченного ресурса в духе пула соединений Sql? (т.е. будет полностью реализован, что это потокобезопасно).
Чтобы выполнить запрос в отношении запроса @Aaronaught для уточнения, пул будет использоваться для запросов балансировки нагрузки к внешней службе. Чтобы поместить это в сценарий, который, вероятно, будет легче сразу понять, в отличие от моей прямой ситуации. У меня есть объект сеанса, который функционирует аналогично ISession
объекту из NHibernate. Каждый уникальный сеанс управляет подключением к базе данных. В настоящее время у меня есть 1 длительный объект сеанса, и я сталкиваюсь с проблемами, когда мой поставщик услуг ограничивает скорость моего использования этого отдельного сеанса.
Из-за того, что они не ожидали, что один сеанс будет рассматриваться как долгосрочная учетная запись службы, они, по-видимому, рассматривают его как клиента, который работает в своем сервисе. Это подводит меня к моему вопросу: вместо одного отдельного сеанса я бы создал пул из разных сеансов и разделил запросы к службе по нескольким сеансам, вместо того чтобы создать единую координационную точку, как я это делал ранее.
Надеюсь, что фон предлагает некоторую ценность, но прямо ответить на некоторые из ваших вопросов:
Q: Дорогие объекты для создания?
A: Ни один объект не является пулом ограниченных ресурсов
В: Будут ли они приобретаться / выпускаться очень часто?
A: Да, еще раз можно подумать о сеансах NHibernate IS, где 1 обычно получается и высвобождается в течение каждого запроса одной страницы.
В: Будет ли достаточно простого «первым пришел - первым обслужен», или вам нужно что-то более умное, то есть, которое предотвратит голод?
A: Простого распределения типа циклического перебора было бы достаточно, под голодом я предполагаю, что вы имеете в виду, если нет доступных сеансов, когда вызывающие абоненты блокируются в ожидании выпусков. Это на самом деле не применимо, поскольку сеансы могут совместно использоваться разными абонентами. Моя цель - распределить использование по нескольким сеансам, а не по одному сеансу.
Я полагаю, что это, вероятно, отклонение от обычного использования пула объектов, поэтому я изначально пропустил эту часть и планировал просто адаптировать шаблон, чтобы разрешить совместное использование объектов, а не позволять ситуации голода когда-либо происходить.
Q: А как насчет таких вещей, как приоритеты, ленивая или энергичная загрузка и т. Д.?
A: Приоритеты не используются, для простоты просто предположим, что я создам пул доступных объектов при создании самого пула.
источник
Ответы:
Объединение объектов в .NET Core
Ядро DotNet имеет реализацию пула объектов добавленного в библиотеку базовых классов (BCL). Вы можете прочитать оригинальную проблему GitHub здесь и просмотреть код для System.Buffers . В настоящее время
ArrayPool
это единственный доступный тип, который используется для объединения массивов. Существует блог хороший пост здесь .Пример его использования можно увидеть в ASP.NET Core. Поскольку ASP.NET Core находится в ядре dotnet BCL, он может совместно использовать свой пул объектов с другими объектами, такими как сериализатор JSON Newtonsoft.Json. Вы можете прочитать этот блог для получения дополнительной информации о том, как Newtonsoft.Json делает это.
Пул объектов в компиляторе Microsoft Roslyn C #
Новый компилятор Microsoft Roslyn C # содержит тип ObjectPool , который используется для объединения часто используемых объектов, которые обычно обновляются и мусор собирается очень часто. Это уменьшает количество и размер операций по сбору мусора, которые должны произойти. Есть несколько различных вложенных реализаций, использующих ObjectPool (см .: Почему в Roslyn так много реализаций Object Pooling? ).
1 - SharedPools - хранит пул из 20 или 100 объектов, если используется BigDefault.
2 - ListPool и StringBuilderPool - строго не отдельные реализации , но обертки вокруг реализации SharedPools , приведенные выше , в частности , для списка и StringBuilder лет. Так что это повторно использует пул объектов, хранящихся в SharedPools.
3 - PooledDictionary и PooledHashSet - они используют ObjectPool напрямую и имеют совершенно отдельный пул объектов. Хранит бассейн из 128 предметов.
Microsoft.IO.RecyclableMemoryStream
Эта библиотека обеспечивает объединение
MemoryStream
объектов. Это замена для заменыSystem.IO.MemoryStream
. У него точно такая же семантика. Он был разработан инженерами Bing. Прочитайте сообщение в блоге здесь или посмотрите код на GitHub .Обратите внимание, что это
RecyclableMemoryStreamManager
должно быть объявлено один раз, и оно будет действовать для всего процесса - это пул. При желании можно использовать несколько пулов.источник
RecyclableMemoryStream
что это удивительное дополнение для ультра оптимизации высокой производительности.Этот вопрос несколько сложнее, чем можно было бы ожидать из-за нескольких неизвестных: поведение пулируемого ресурса, ожидаемое / требуемое время жизни объектов, реальная причина, по которой требуется пул, и т. Д. Обычно пулы являются специальными - поток пулы, пулы соединений и т. д., поскольку их легче оптимизировать, когда вы точно знаете, что делает ресурс, и, что более важно, можете контролировать его реализацию.
Поскольку это не так просто, я попытался предложить довольно гибкий подход, с которым вы можете поэкспериментировать и посмотреть, что работает лучше всего. Заранее извиняюсь за длинный пост, но есть много оснований, которые нужно покрыть, когда дело доходит до реализации достойного пула ресурсов общего назначения. и я действительно только царапаю поверхность.
У пула общего назначения должно быть несколько основных «настроек», включая:
Для механизма загрузки ресурсов .NET уже дает нам чистую абстракцию - делегаты.
Передайте это через конструктор пула, и мы закончили с этим. Использование универсального типа с
new()
ограничением также работает, но это более гибко.Из двух других параметров стратегия доступа является более сложным, поэтому мой подход заключался в использовании подхода на основе наследования (интерфейса):
Концепция здесь проста - мы позволим общедоступному
Pool
классу обрабатывать общие проблемы, такие как безопасность потоков, но будем использовать разные «хранилища элементов» для каждого шаблона доступа. LIFO легко представляется стеком, FIFO является очередью, и я использовал не очень оптимизированную, но, вероятно, адекватную реализацию циклического буфера, используяList<T>
указатель и индекс для аппроксимации шаблона циклического доступа.Все нижеприведенные классы являются внутренними классами
Pool<T>
- это был выбор стиля, но, поскольку они действительно не предназначены для использования внеPool
, это имеет смысл.Это очевидные - стек и очередь. Я не думаю, что они действительно заслуживают большого объяснения. Круговой буфер немного сложнее:
Я мог бы выбрать несколько разных подходов, но суть в том, что ресурсы должны быть доступны в том же порядке, в котором они были созданы, что означает, что мы должны сохранять ссылки на них, но отмечать их как «используемые» (или нет). ). В худшем случае доступен только один слот, и для каждой выборки требуется полная итерация буфера. Это плохо, если у вас есть сотни объединенных ресурсов и вы получаете и выпускаете их несколько раз в секунду; на самом деле это не проблема для пула из 5-10 предметов, и в типичном случае, когда ресурсы используются слабо, нужно продвинуть только один или два слота.
Помните, что эти классы являются закрытыми внутренними классами - поэтому им не нужно много проверять ошибки, сам пул ограничивает доступ к ним.
Добавьте перечисление и фабричный метод, и мы закончили с этой частью:
Следующая проблема, которую нужно решить, это стратегия загрузки. Я определил три типа:
Первые два должны быть самоочевидными; третий - своего рода гибрид, он лениво загружает ресурсы, но фактически не начинает повторно использовать какие-либо ресурсы, пока пул не будет заполнен. Это было бы хорошим компромиссом, если вы хотите, чтобы пул был полон (что звучит так, как вы), но хотите отложить расходы на их фактическое создание до первого доступа (то есть, чтобы улучшить время запуска).
Методы загрузки действительно не слишком сложны, теперь, когда у нас есть абстракция хранилища элементов:
Приведенные выше поля
size
иcount
относятся к максимальному размеру пула и общему количеству ресурсов, принадлежащих пулу (но не обязательно доступных ), соответственно.AcquireEager
Проще всего, предполагается, что элемент уже находится в магазине - эти элементы будут предварительно загружены при создании, т. е. вPreloadItems
методе, показанном последним.AcquireLazy
проверяет, есть ли в пуле свободные предметы, а если нет, то создает новый.AcquireLazyExpanding
создаст новый ресурс, если пул еще не достиг целевого размера. Я попытался оптимизировать это, чтобы минимизировать блокировку, и я надеюсь, что не сделал никаких ошибок (у меня есть испытал это при многопоточных условиях, но , очевидно , не исчерпывающе).Вы можете спросить, почему ни один из этих методов не потрудился проверить, достиг ли магазин максимального размера. Я вернусь к этому через минуту.
Теперь для самого бассейна. Вот полный набор личных данных, некоторые из которых уже были показаны:
Отвечая на вопрос, который я затмил в последнем абзаце - как обеспечить ограничение общего количества созданных ресурсов - выясняется, что в .NET уже есть достаточно хороший инструмент для этого, он называется Семафор и разработан специально для разрешения фиксированных количество потоков доступа к ресурсу (в данном случае «ресурс» является внутренним хранилищем элементов). Поскольку мы не внедряем полноценную очередь производителей / потребителей, это вполне соответствует нашим потребностям.
Конструктор выглядит так:
Здесь не должно быть никаких сюрпризов. Единственное, что следует отметить, это специальный корпус для быстрой загрузки, используя
PreloadItems
метод, уже показанный ранее.Поскольку к настоящему моменту практически все абстрагировано, фактические методы
Acquire
иRelease
методы очень просты:Как объяснялось ранее, мы используем
Semaphore
контроль параллелизма вместо религиозной проверки состояния хранилища элементов. Пока приобретенные предметы правильно выпущены, не о чем беспокоиться.Последнее, но не менее важное, есть очистка:
Цель этого
IsDisposed
свойства станет ясна через мгновение. Все, чтоDispose
действительно делает основной метод, - это удаляет фактически объединенные элементы, если они реализуютсяIDisposable
.Теперь вы можете использовать это как есть, с
try-finally
блоком, но мне не нравится этот синтаксис, потому что, если вы начнете передавать объединенные ресурсы между классами и методами, это станет очень запутанным. Возможно, что основной класс, который использует ресурс, даже не имеет ссылки на пул. Это действительно становится довольно грязным, поэтому лучший подход - создать «умный» объект из пула.Допустим, мы начнем со следующего простого интерфейса / класса:
Вот наш притворный одноразовый
Foo
ресурс, который реализуетIFoo
и имеет некоторый шаблонный код для генерации уникальных идентификаторов. Что мы делаем, это создаем еще один особый объект в виде пула:Это просто передает все «настоящие» методы к его внутреннему
IFoo
(мы могли бы сделать это с помощью библиотеки Dynamic Proxy, такой как Castle, но я не буду вдаваться в подробности). Он также поддерживает ссылку на тотPool
, кто его создает, так что когда мыDispose
этот объект, он автоматически выпускает себя обратно в пул. За исключением случаев, когда пул уже был удален - это означает, что мы находимся в режиме «очистки», и в этом случае он фактически очищает внутренний ресурс .Используя вышеприведенный подход, мы получаем такой код:
Это очень хорошая вещь, чтобы быть в состоянии сделать. Это означает , что код , который использует
IFoo
(в отличие от кода , который создает его) на самом деле не нужно быть в курсе бассейна. Вы даже можете вводитьIFoo
объекты, используя вашу любимую библиотеку DI и вPool<T>
качестве провайдера / фабрики.Я поместил полный код на PasteBin для вашего удобства копирования и вставки. Есть также небольшая тестовая программа, которую вы можете использовать для работы с различными режимами загрузки / доступа и многопоточными условиями, чтобы убедиться, что она поточнобезопасна и не глючит.
Дайте мне знать, если у вас есть какие-либо вопросы или опасения по этому поводу.
источник
Нечто подобное может удовлетворить ваши потребности.
Пример использования
источник
Put
метода и оставить для простоты какой-то тип проверки того, является ли объект неисправным, и создать новый экземпляр для добавления в пул вместо вставки предыдущего?Пример из MSDN: Как создать пул объектов с помощью ConcurrentBag
источник
В свое время Microsoft предоставила платформу через Microsoft Transaction Server (MTS), а затем и COM + для создания пула объектов для COM-объектов. Эта функциональность была перенесена в System.EnterpriseServices в .NET Framework, а теперь и в Windows Communication Foundation.
Объединение объектов в WCF
Эта статья написана на .NET 1.1, но все еще должна применяться в текущих версиях Framework (даже если WCF является предпочтительным методом).
Объединение объектов .NET
источник
IInstanceProvider
интерфейс существует, так как я буду реализовывать это для своего решения. Я всегда любил размещать свой код за предоставленным Microsoft интерфейсом, когда они дают подходящее определение.Мне очень нравится реализация Аронота, особенно потому, что он обрабатывает ожидающий ресурс, который становится доступным благодаря использованию семафора. Есть несколько дополнений, которые я хотел бы сделать:
sync.WaitOne()
вsync.WaitOne(timeout)
и выставить таймаут в качестве параметра наAcquire(int timeout)
методе. Это также потребует обработки условия, когда поток истекает, ожидая, когда объект станет доступным.Recycle(T item)
метод для обработки ситуаций, когда объект должен быть переработан, например, в случае сбоя.источник
Это еще одна реализация, с ограниченным количеством объектов в пуле.
источник
Эта статья, ориентированная на Java, представляет шаблон пула connectionImpl и шаблон абстрагированного объекта и может быть хорошим первым подходом: http://www.developer.com/design/article.php/626171/Pattern-Summaries-Object-Pool. HTM
Шаблон пула объектов:
источник
Расширение msdn как создать пул объектов с помощью ConcurrentBag.
https://github.com/chivandikwa/ObjectPool
источник
Вы можете использовать пакет Nuget
Microsoft.Extensions.ObjectPool
Документация здесь:
https://docs.microsoft.com/en-us/aspnet/core/performance/objectpool?view=aspnetcore-3.1 https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.objectpool
источник