generated from mmistakes/mm-github-pages-starter
-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
082a013
commit b0eab6b
Showing
1 changed file
with
320 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,320 @@ | ||
--- | ||
title: "استفاده از Pool در .Net برای Channel RabbitMQ و Database Connection" | ||
categories: | ||
- Net | ||
tags: | ||
- net | ||
- pool | ||
- rabbitmq | ||
--- | ||
|
||
در صورتی که در برنامه خود نیاز دارید تا از Pool برای دریافت موارد مختلف مانند RabbitMQ Channel به دلیل جلوگیری از سربار ایجاد هردفعه آن استفاده کنید، میتوانید از کد زیر کمک بگیرید. | ||
این کد بصورت جنریک است و علاوه بر مورد گفته شده میتوانید از آن در جاهای مختلف مانند کانکشن دیتابیس نیز استفاده کنید. | ||
|
||
Interface: | ||
|
||
```csharp | ||
namespace Pool; | ||
|
||
/// <summary> | ||
/// Easy Pool | ||
/// </summary> | ||
/// <typeparam name="T">The type of objects to be pooled.</typeparam> | ||
public interface IPool<T> : IDisposable where T : class | ||
{ | ||
/// <summary> | ||
/// Retrieves an item from the pool. | ||
/// </summary> | ||
/// <returns>An item from the pool.</returns> | ||
/// <exception cref="InvalidOperationException">Thrown when the pool fails to create a new resource.</exception> | ||
T GetFromPool(); | ||
|
||
/// <summary> | ||
/// Returns an item back to the pool. | ||
/// </summary> | ||
/// <param name="item">The item to return to the pool.</param> | ||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="item"/> is null.</exception> | ||
void ReturnToPool(T item); | ||
|
||
/// <summary> | ||
/// Get the number of remaining threads that can enter | ||
/// </summary> | ||
/// <returns></returns> | ||
public int GetCurrentCount(); | ||
|
||
/// <summary> | ||
/// Get the current size of the pool. | ||
/// </summary> | ||
/// <returns></returns> | ||
public int GetCurrentSize(); | ||
|
||
/// <summary> | ||
/// Get the maximum size of the pool. | ||
/// </summary> | ||
/// <returns></returns> | ||
public int GetMaxSize(); | ||
|
||
/// <summary> | ||
/// Get the available size of the pool. | ||
/// </summary> | ||
/// <returns></returns> | ||
public int GetAvailableSize(); | ||
} | ||
``` | ||
|
||
کد: | ||
|
||
```csharp | ||
using System.Collections.Concurrent; | ||
|
||
namespace Pool; | ||
|
||
/// <summary> | ||
/// Easy Pool | ||
/// </summary> | ||
/// <typeparam name="T">The type of objects to be pooled.</typeparam> | ||
public class Pool<T> : IPool<T> where T : class | ||
{ | ||
private readonly SemaphoreSlim _semaphore; | ||
private readonly ConcurrentBag<T> _items; | ||
private readonly Func<T> _factory; | ||
private readonly int _maxPoolSize; | ||
private int _currentSize; | ||
private bool _disposed; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="Pool{T}"/> class. | ||
/// </summary> | ||
/// <param name="factory">A function to create new instances of <typeparamref name="T"/>.</param> | ||
/// <param name="initPoolSize">The initial number of objects to be created and added to the pool. Default is 100.</param> | ||
/// <param name="maxPoolSize">The maximum number of objects that can be in the pool. Default is <see cref="int.MaxValue"/>.</param> | ||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="factory"/> is null.</exception> | ||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="initPoolSize"/> is negative or <paramref name="maxPoolSize"/> is less than or equal to zero.</exception> | ||
/// <exception cref="ArgumentException">Thrown when <paramref name="maxPoolSize"/> is less than <paramref name="initPoolSize"/>.</exception> | ||
public Pool(Func<T> factory, int initPoolSize = 100, int maxPoolSize = int.MaxValue) | ||
{ | ||
#if NET8_0 | ||
ArgumentNullException.ThrowIfNull(factory); | ||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(initPoolSize); | ||
#else | ||
if (factory is null) | ||
{ | ||
throw new ArgumentNullException(nameof(factory)); | ||
} | ||
|
||
if (initPoolSize < 1) | ||
{ | ||
throw new ArgumentNullException(nameof(initPoolSize)); | ||
} | ||
#endif | ||
|
||
if (maxPoolSize <= 0) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(maxPoolSize), Resources.Max_pool_Size_Min_Value); | ||
} | ||
|
||
if (maxPoolSize < initPoolSize) | ||
{ | ||
throw new ArgumentException(Resources.Max_Pool_Size_More_Than_Init); | ||
} | ||
|
||
_items = []; | ||
_factory = factory; | ||
_maxPoolSize = maxPoolSize; | ||
_currentSize = initPoolSize; | ||
_semaphore = new SemaphoreSlim(maxPoolSize); | ||
|
||
for (var i = 0; i < initPoolSize; i++) | ||
{ | ||
_items.Add(Create()); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Retrieves an item from the pool. | ||
/// </summary> | ||
/// <returns>An item from the pool.</returns> | ||
/// <exception cref="InvalidOperationException">Thrown when the pool fails to create a new resource.</exception> | ||
public T GetFromPool() | ||
{ | ||
_semaphore.Wait(); | ||
|
||
try | ||
{ | ||
var item = _items.TryTake(out var result) ? result : TryCreate(); | ||
|
||
if (item == null) | ||
{ | ||
_ = _semaphore.Release(); | ||
throw new InvalidOperationException(Resources.Failed_Create_Resource); | ||
} | ||
|
||
return item; | ||
} | ||
catch | ||
{ | ||
_ = _semaphore.Release(); | ||
throw; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Returns an item back to the pool. | ||
/// </summary> | ||
/// <param name="item">The item to return to the pool.</param> | ||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="item"/> is null.</exception> | ||
public void ReturnToPool(T item) | ||
{ | ||
#if NET8_0 | ||
ArgumentNullException.ThrowIfNull(item); | ||
#else | ||
if (item is null) | ||
{ | ||
throw new ArgumentNullException(nameof(item)); | ||
} | ||
#endif | ||
|
||
_items.Add(item); | ||
_semaphore.Release(); | ||
} | ||
|
||
/// <summary> | ||
/// Get the number of remaining threads that can enter | ||
/// </summary> | ||
/// <returns></returns> | ||
public int GetCurrentCount() => _semaphore.CurrentCount; | ||
|
||
/// <summary> | ||
/// Get the current size of the pool. | ||
/// </summary> | ||
/// <returns></returns> | ||
public int GetCurrentSize() => _currentSize; | ||
|
||
/// <summary> | ||
/// Get the maximum size of the pool. | ||
/// </summary> | ||
/// <returns></returns> | ||
public int GetMaxSize() => _maxPoolSize; | ||
|
||
/// <summary> | ||
/// Get the available size of the pool. | ||
/// </summary> | ||
/// <returns></returns> | ||
public int GetAvailableSize() => _maxPoolSize - _currentSize; | ||
|
||
/// <summary> | ||
/// Disposes the pool and releases all resources. | ||
/// </summary> | ||
public void Dispose() | ||
{ | ||
Dispose(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
/// <summary> | ||
/// Disposes the pool and releases all resources. | ||
/// </summary> | ||
/// <param name="disposing">A boolean value indicating whether the method is called from the Dispose method.</param> | ||
protected virtual void Dispose(bool disposing) | ||
{ | ||
if (_disposed) | ||
{ | ||
return; | ||
} | ||
|
||
if (disposing) | ||
{ | ||
_semaphore.Dispose(); | ||
|
||
while (_items.TryTake(out var item)) | ||
{ | ||
if (item is IDisposable disposable) | ||
{ | ||
disposable.Dispose(); | ||
} | ||
} | ||
} | ||
|
||
_disposed = true; | ||
} | ||
|
||
private T Create() | ||
{ | ||
try | ||
{ | ||
var item = _factory(); | ||
|
||
return item ?? throw new InvalidOperationException(Resources.Factory_Produced_Null_Item); | ||
} | ||
catch (Exception ex) | ||
{ | ||
throw new InvalidOperationException(Resources.Erro_Creation, ex); | ||
} | ||
} | ||
|
||
private T TryCreate() | ||
{ | ||
var newSize = Interlocked.Increment(ref _currentSize); | ||
|
||
if (newSize >= _maxPoolSize) | ||
{ | ||
_ = Interlocked.Decrement(ref _currentSize); | ||
throw new InvalidOperationException(Resources.Poo_Maximum_Capacity); | ||
} | ||
|
||
try | ||
{ | ||
return Create(); | ||
} | ||
catch | ||
{ | ||
_ = Interlocked.Decrement(ref _currentSize); | ||
throw; | ||
} | ||
} | ||
} | ||
|
||
``` | ||
|
||
نمونه استفاده: | ||
|
||
```csharp | ||
using Pool; | ||
|
||
var pool = new Pool<object>(() => new object(), initPoolSize: 5, maxPoolSize: 10); | ||
|
||
try | ||
{ | ||
var obj = pool.Get(); | ||
pool.Return(obj); | ||
} | ||
finally | ||
{ | ||
pool.Dispose(); | ||
} | ||
``` | ||
|
||
نمونه کد برای ربیت: | ||
|
||
```csharp | ||
using Pool; | ||
|
||
_connection = factory.CreateConnection(endpoints); | ||
|
||
var pool = new Pool<IModel>(() => _connection.CreateModel(), initPoolSize: 5, maxPoolSize: 10); | ||
|
||
try | ||
{ | ||
var obj = pool.Get(); | ||
pool.Return(obj); | ||
} | ||
finally | ||
{ | ||
pool.Dispose(); | ||
} | ||
``` | ||
|
||
مورد بالا برای راحتی بصورت Nuget Package نیز درآمده است که میتوانید از آن استفاده کنید. | ||
|
||
[EasyPool github](https://github.com/MHKarami97/Pool) | ||
[EasyPool nuget](https://www.nuget.org/packages/EasyPool/) |