实现具有时间限制的池

时间:2015-07-14 15:24:17

标签: c# multithreading pool

继续这个问题:

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实例。

1 个答案:

答案 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[];