继续这个问题:
Correct way to implement a resource pool
我现在正考虑实现允许池的用户继续保留池中对象的最长时间,但我不确定实现这样的事情的正确方法。所以,说我们有这样的事情:
IFoo GetFooFromPool()
{
if (_sem.Wait(WaitTimeOut))
{
return Pop();
}
throw new WaitTimeoutException("Timed out waiting for Foo instance");
}
所以_sem
是一个信号量,Pop
将从堆栈弹出一个实例(如果需要,初始化它),到目前为止一直很好。
当调用者使用IFoo
完成后,将其返回堆栈,如下所示:
void ReleaseFoo(IFoo p)
{
if (p == null)
{
throw new ArgumentNullException("Foo parameter is null");
}
Push(p);
_sem.Release();
}
我实际上使用IDisposable
用一个包装类来包装所有这些,以确保Foo
返回到池中。所以从客户端来看,它看起来像这样:
using (var f = myPool.GetDisposableFoo())
{
// Do stuff...
}
我想要做的是,如果// Do Stuff
花费太长时间(或可能挂起),IFoo
将被回收,调用代码将抛出异常。
所以我想做这样的事情:
private IFoo Pop()
{
IFoo p;
_stack.TryPop(out p);
if (p == null)
{
p = RunFactory(); // this is lazy initialization of the stack
}
if (MaximumLoanTime > 0)
{
p.CurrentToken = new CancellationTokenSource();
Task.Delay(MaximumLoanTime, p.CurrentToken.Token).ContinueWith(() =>
{
// Timeout, return to the stack
// Inform current owner???
Push(p);
});
}
return p;
}
问题是如何确保当前持有者发出信号,以便它不会继续尝试使用现在可能被其他人使用的IFoo
实例。
答案 0 :(得分:0)
因此,我最终向LoanExpired
添加了OnLoanExpired
个事件和IFoo
方法。所以现在它的工作原理如下:
private IFoo Pop()
{
IFoo p;
_stack.TryPop(out p);
if (p == null)
{
p = RunFactory(); // this is lazy initialization of the stack
}
if (MaximumLoanTime > 0)
{
p.CurrentToken = new CancellationTokenSource();
Task.Delay(MaximumLoanTime, p.CurrentToken.Token).ContinueWith(() =>
{
p.CurrentToken.Dispose();
p.OnLoanExpired();
}, TaskContinuationOptions.NotOnCanceled);
}
return p;
}
现在,将过期的贷款对象推迟到我的FooWrapper
,该LoanExpired
订阅了其Foo
实例的private void foo_LoanExpired(object sender, EventArgs e)
{
isDisposed = true; // this is checked on any attempt to use this object
foo.LoanExpired -= foo_LoanExpired;
_pool.ReturnExpiredLoan(foo);
OnLoanExpired(); // pass event up to any client handlers
}
事件:
ReturnExpiredLoan
回到我的游泳池,void ReturnExpiredLoan(IFoo p)
{
// Since it's expired, there is a risk it is broken or hung up
// so we push null back on the stack to force a fresh instance next time around
_stack.Push(null);
_sem.Release();
p.Dispose();
}
看起来像这样:
Foo
现在我决定实际上只是转储过期的FooWrapper
并在我的特定情况下创建一个新的,但对于不同的资源来说,这不一定是正确的方法。
还有其他几项更改,当我的public void Dispose()
{
foo.LoanExpired -= foo_LoanExpired;
if (!isDisposed) // don't try and return an instance if it was killed already
{
_pool.ReleaseFoo(foo);
}
}
被处理时,我必须取消订阅事件处理程序:
Foo
当我将private void Push(IFoo p)
{
if (p.CurrentToken != null)
{
p.CurrentToken.Cancel();
p.CurrentToken.Dispose();
p.CurrentToken = null;
}
_stack.Push(p);
}
推回堆栈时,我需要取消过期并处理令牌源:
data *bad[];