【问题标题】:Implementing a pool with a time limit实现一个有时间限制的池
【发布时间】:2015-10-03 08:07:36
【问题描述】:

从这个问题继续:

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

【问题讨论】:

    标签: c# multithreading pool


    【解决方案1】:

    所以我最终添加了一个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 订阅它正在包装的Foo 实例的LoanExpired 事件:

    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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-23
      • 2014-10-09
      • 2021-02-05
      • 1970-01-01
      • 2011-09-11
      • 1970-01-01
      • 2011-11-26
      相关资源
      最近更新 更多