2015-07-14 3 views
1

Продолжает от этого вопроса:Реализация бассейна с ограничением по времени

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

Итак, в итоге я добавил 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); 
} 
Смежные вопросы