2015-03-03 25 views
5

C# 'de iş parçacığı kullanmayı deniyorum ve sonuç olarak aşağıdaki sınıfı oluşturdum. Herhangi bir yarış koşulundan kaçınmaya çalıştım, ancak kullanımda bir kilitlenme meydana geldi.Nesne havuzu sınıfındaki kilitlenme

Sınıf, iki farklı kilit, basit işlemler için bir döndürme kilidi ve ayrıca hiçbir nesnenin hazır olmaması durumunda beklemek üzere bir Monitor kilidi kullanır. Orijinal olarak EventWaitHandle kullanmıştım, ancak WaitOne/Set önceliğinden dolayı yarış koşullarının kaçınılmaz olduğunu keşfettim.

Monitor.Pulse'un Monitor.Wait'dan önce gelemediğini unutmayın, bu nedenle kilitlenme neden başka ne olabilir? 5 iş parçacığının 4 kapasiteli bir TestPool sınıfını kullanması durumunda, kilitlenme her zaman düzensiz bir anda SpinLock'da gerçekleşir.

internal class TestPool<T> where T : class 
{ 
    private int capacity; 
    private int unitPos; 
    private int waitUnitPos; 
    private int waitCount; 
    private int lockState; 
    private object lockObj; 
    private T[] units; 
    private Func<T> unitFactory; 

    public TestPool(int capacity, Func<T> unitFactory) 
    { 
     this.lockObj = new object(); 
     this.unitFactory = unitFactory; 

     Init(capacity); 
    } 

    public T Fetch() 
    { 
     T unit; 

     Lock(); 
     unit = (unitPos != capacity) ? units[unitPos++] : Wait(); 
     Unlock(); 

     return unit; 
    } 

    public void Store(T unit) 
    { 
     Lock(); 

     if (waitCount == 0) 
     { 
      units[--unitPos] = unit; 
     } 
     else 
     { 
      Pulse(unit); 
     } 

     Unlock(); 
    } 

    private T Wait() 
    { 
     waitCount++; 

     lock (lockObj) 
     { 
      Unlock(); 
      Monitor.Wait(lockObj); 
      Lock(); 

      return units[--waitUnitPos]; 
     } 
    } 

    private void Pulse(T unit) 
    { 
     waitCount--; 
     units[waitUnitPos++] = unit; 

     lock (lockObj) 
     { 
      Monitor.Pulse(lockObj); 
     } 
    } 

    private void Lock() 
    { 
     if (Interlocked.CompareExchange(ref lockState, 1, 0) != 0) 
     { 
      SpinLock(); 
     } 
    } 

    private void SpinLock() 
    { 
     SpinWait spinWait = new SpinWait(); 

     do 
     { 
      spinWait.SpinOnce(); 
     } 
     while (Interlocked.CompareExchange(ref lockState, 1, 0) != 0); 
    } 

    private void Unlock() 
    { 
     Interlocked.Exchange(ref lockState, 0); 
    } 

    private void Init(int capacity) 
    { 
     T[] tx = new T[capacity]; 

     for (int i = 0; i < capacity; i++) 
     { 
      tx[i] = unitFactory.Invoke(); 
     } 

     units = tx; 
     this.capacity = capacity; 
    } 
} 
+0

Spinlocks dışlama http://stackoverflow.com/questions/5869825/when-should-one-use-a-spinlock-instead-of-mutex vs Bu sorununuzu çözecektir söyleyerek, ama bir şey değil içine bak. – JNYRanger

+0

SetCapacity'yi ctorun dışında kullanıyor musunuz? – usr

+0

@usr - Bu amaçlanmıştır, ancak kilitlemeyi atlamak için bunun özel bir sürümünü türeteceğim. Herhangi bir önlemin kasıtlı olarak bulunmadığını unutmayın. – Tcqqp

cevap

0

Düzeltildi. Aşağıdaki kodu Monitor kilidi dışında yerleştirmek zorunda kaldım.

Lock(); 

return units[--waitUnitPos];