gpt4 book ai didi

c# - 什么是实现对象池的好方法?

转载 作者:行者123 更新时间:2023-11-30 14:55:19 32 4
gpt4 key购买 nike

我有一个第三方类,我们称之为Analyser。这个类真的很擅长分析,但是实例化很昂贵(秒)并且不支持多线程。

我的应用程序需要处理涉及调用 Analyser 的请求。这些请求将同时发生。

我想我需要创建一个通用类,比如

public class Pool<T>
{
public Pool(Func<T> instantiator, int size)
{
...
}

public async Task<TResult> Invoke<TResult>(
Func<T, TResult> target,
CancellationToken cancellationToken)
{
// await the first available T,
// lock the T,
// invoke the target, return the result
// release the lock
}
}

此类通常会封装池化功能。

我的问题是,实现这个类的正确方法是什么。它是否已经以不同的名称存在?我应该使用 TPL.DataFlow 吗?我应该用手滚动吗?

被定义为可靠的线程安全,越容易维护越好。


如果通用 Pool 是解决问题的错误方法,请提出正确的替代方法。


Pool 类将像这样使用。

private readonly Pool<Analyser> pool = new Pool<Analyser>(
() => new Analyser(a, b, c),
100);

public async Task<string> ProcessRequest(
string raw,
CancellationToken cancellationToken)
{
return await this.pool.Invoke(
analyser => analyser.Analyse(raw),
cancellationToken);
}

最佳答案

我认为构建通用池将是一项相当复杂的任务,因此我会从中获得很多乐趣:-)

注意:我的观点与您的观点不同的最重要的一点是,我不希望池处理与其管理的对象相关的线程问题。该池有一些与线程安全相关的代码,但仅用于管理它自己的状态(实例列表)。线程启动、停止/和/或取消是池的客户端和构造对象的关注点,而不是池本身的关注点。

我会开始:

  1. 用于池维护对象的一次性包装器,在处理后将对象返回池
  2. 构建或重用可用实例并在将实例返回给客户端之前包装它们的池。

super 简化的实现:

class PoolItem<T> : IDisposable
{
public event EventHandler<EventArgs> Disposed;


public PoolItem(T wrapped)
{
WrappedObject = wrapped;
}


public T WrappedObject { get; private set; }


public void Dispose()
{
Disposed(this, EventArgs.Empty);
}
}

现在是游泳池:

class Pool<T> where T : class
{
private static readonly object m_SyncRoot = new object();

private readonly Func<T> m_FactoryMethod;
private List<T> m_PoolItems = new List<T>();


public Pool(Func<T> factoryMethod)
{
m_FactoryMethod = factoryMethod;
}


public PoolItem<T> Get()
{
T target = null;

lock (m_SyncRoot)
{
if (m_PoolItems.Count > 0)
{
target = m_PoolItems[0];
m_PoolItems.RemoveAt(0);
}
}

if (target == null)
target = m_FactoryMethod();

var wrapper = new PoolItem<T>(target);
wrapper.Disposed += wrapper_Disposed;

return wrapper;
}


void wrapper_Disposed(object sender, EventArgs e)
{
var wrapper = sender as PoolItem<T>;

lock (m_SyncRoot)
{
m_PoolItems.Add(wrapper.WrappedObject);
}
}
}

用法:

class ExpensiveConstructionObject
{
public ExpensiveConstructionObject()
{
Console.WriteLine("Executing the expensive constructor...");
}

public void Do(string stuff)
{
Console.WriteLine("Doing: " + stuff);
}
}

class Program
{
static void Main(string[] args)
{
var pool = new Pool<ExpensiveConstructionObject>(() => new ExpensiveConstructionObject());

var t1 = pool.Get();
t1.WrappedObject.Do("task 1");

using (var t2 = pool.Get())
t2.WrappedObject.Do("task 2");

using (var t3 = pool.Get())
t3.WrappedObject.Do("task 3");

t1.Dispose();

Console.ReadLine();
}
}

接下来的步骤是:

  1. 经典池功能,如:初始大小、最大大小
  2. 动态代理,允许 Pool::Get 返回类型为 T,而不是 PoolItem
  3. 维护包装器列表,如果调用者没有在 Pool 自行处理时处理它们

关于c# - 什么是实现对象池的好方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25639756/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com