gpt4 book ai didi

c# - 资源池的正确实现方式

转载 作者:行者123 更新时间:2023-12-02 15:27:46 25 4
gpt4 key购买 nike

我正在尝试实现一些管理资源池的东西,这样调用代码就可以请求一个对象,如果可用,就会从池中获得一个对象,否则就会等待。但是,我无法使同步正常工作。我在池类中的内容是这样的(其中 autoEventAutoResetEvent 最初设置为有信号:

public Foo GetFooFromPool()
{
autoEvent.WaitOne();
var foo = Pool.FirstOrDefault(p => !p.InUse);
if (foo != null)
{
foo.InUse = true;
autoEvent.Set();
return foo;
}
else if (Pool.Count < Capacity)
{
System.Diagnostics.Debug.WriteLine("count {0}\t capacity {1}", Pool.Count, Capacity);
foo = new Foo() { InUse = true };
Pool.Add(foo);
autoEvent.Set();
return foo;
}
else
{
return GetFooFromPool();
}
}

public void ReleaseFoo(Foo p)
{
p.InUse = false;
autoEvent.Set();
}

这个想法是当你调用 GetFooFromPool 时,你等待直到收到信号,然后你尝试找到一个现有的未使用的 Foo。如果找到,我们将其设置为 InUse,然后发出信号以便其他线程可以继续。如果找不到,我们会检查池是否已满。如果没有,我们创建一个新的 Foo,将其添加到池中并再次发出信号。如果这些条件都不满足,我们将通过再次调用 GetFooFromPool 再次等待。

现在在 ReleaseFoo 中,我们只是将 InUse 设置回 false,并向等待在 GetFooFromPool 中的下一个线程发出信号(如果有的话)尝试和得到一个 Foo

问题似乎出在我管理池的大小上。容量为 5,我以 6 Foo 结束。我可以在我的调试行中看到 count 0 出现了几次并且 count 1 也可能出现了几次。很明显,我有多个线程进入 block ,据我所知,它们不应该进入。

我在这里做错了什么?

编辑:像这样的双重检查锁:

else if (Pool.Count < Capacity)
{
lock(locker)
{
if (Pool.Count < Capacity)
{
System.Diagnostics.Debug.WriteLine("count {0}\t capacity {1}", Pool.Count, Capacity);
foo = new Foo() { InUse = true };
Pool.Add(foo);
autoEvent.Set();
return foo;
}
}
}

似乎确实解决了问题,但我不确定这是最优雅的方法。

最佳答案

正如评论中已经提到的,计数信号量是您的 friend 。将其与并发堆栈相结合,您将获得一个非常简单、线程安全的实现,您仍然可以在其中延迟分配池项。

下面的基本实现提供了这种方法的示例。请注意,这里的另一个优点是您不需要使用 InUse 成员作为跟踪内容的标志来“污染”您的池项目。

请注意,作为微优化,在这种情况下,堆栈优先于队列,因为它将提供池中最近返回的实例,该实例可能仍在例如一级缓存。

public class GenericConcurrentPool<T> : IDisposable where T : class
{
private readonly SemaphoreSlim _sem;
private readonly ConcurrentStack<T> _itemsStack;
private readonly Action<T> _onDisposeItem;
private readonly Func<T> _factory;

public GenericConcurrentPool(int capacity, Func<T> factory, Action<T> onDisposeItem = null)
{
_itemsStack = new ConcurrentStack<T>(new T[capacity]);
_factory = factory;
_onDisposeItem = onDisposeItem;
_sem = new SemaphoreSlim(capacity);
}

public async Task<T> CheckOutAsync()
{
await _sem.WaitAsync();
return Pop();
}

public T CheckOut()
{
_sem.Wait();
return Pop();
}

public void CheckIn(T item)
{
Push(item);
_sem.Release();
}

public void Dispose()
{
_sem.Dispose();
if (_onDisposeItem != null)
{
T item;
while (_itemsStack.TryPop(out item))
{
if (item != null)
_onDisposeItem(item);
}
}
}

private T Pop()
{
T item;
var result = _itemsStack.TryPop(out item);
Debug.Assert(result);
return item ?? _factory();
}

private void Push(T item)
{
Debug.Assert(item != null);
_itemsStack.Push(item);
}
}

关于c# - 资源池的正确实现方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29501285/

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