C# ReaderWriterLockSlim 實現

其實ReaderWriterLockSlim的實現前段時間看了,當時不打算記錄下來的,由於它的實現實在System.Core項目裏面,而不是mscorlib項目。按照慣例咱們仍是先看看網上的一些說法吧。數據庫

讀寫鎖 ReaderWriterLock 、就是支持單個寫線程和多個讀線程的鎖。自.NET 3.5 開始 ReaderWriterLockSlim登上舞臺,ReaderWriterLockSlim 能夠看作是 ReaderWriterLock 的升級版。 因爲 ReaderWriterLockSlim 默認不支持遞歸調用、因此在某種意義上來講更不容易形成死鎖。
ReaderWriterLockSlim 類支持三種鎖定模式:Read,Write,UpgradeableRead。這三種模式對應的方法分別是 EnterReadLock,EnterWriteLock,EnterUpgradeableReadLock 。再就是與此對應的 TryEnterReadLock,TryEnterWriteLock,TryEnterUpgradeableReadLock,ExitReadLock,ExitWriteLock,ExitUpgradeableReadLock。Read 和 Writer 鎖定模式比較簡單易懂:Read 模式是典型的共享鎖定模式,任意數量的線程均可以在該模式下同時得到鎖;Writer 模式則是互斥模式,在該模式下只容許一個線程進入該鎖。UpgradeableRead 鎖定模式可能對於大多數人來講比較新鮮,可是在數據庫領域卻衆所周知。
備註及注意事項
一、對於同一把鎖、多個線程可同時進入讀模式。
二、對於同一把鎖、同時只容許一個線程進入寫模式。
三、對於同一把鎖、同時只容許一個線程進入可升級的讀模式。
四、經過默認構造函數建立的讀寫鎖是不支持遞歸的,若想支持遞歸 可經過構造 ReaderWriterLockSlim(LockRecursionPolicy) 建立實例。
五、對於同一把鎖、同一線程不可兩次進入同一鎖狀態(開啓遞歸後能夠)
六、對於同一把鎖、即使開啓了遞歸、也不能夠在進入讀模式後再次進入寫模式或者可升級的讀模式(在這以前必須退出讀模式)。
七、再次強調、不建議啓用遞歸。
八、讀寫鎖具備線程關聯性,即兩個線程間擁有的鎖的狀態相互獨立不受影響、而且不能相互修改其鎖的狀態。
九、升級狀態:在進入可升級的讀模式 EnterUpgradeableReadLock後,可在恰當時間點經過EnterWriteLock進入寫模式。
十、降級狀態:可升級的讀模式能夠降級爲讀模式:即在進入可升級的讀模式EnterUpgradeableReadLock後, 經過首先調用讀取模式EnterReadLock方法,而後再調用 ExitUpgradeableReadLock 方法ide

實現code以下:函數

public enum LockRecursionPolicy
{
    NoRecursion = 0,
    SupportsRecursion = 1,
}
internal class ReaderWriterCount
{    
    public long lockID;
    public int readercount;
    public int writercount;
    public int upgradecount;
    public ReaderWriterCount next;
}

public class ReaderWriterLockSlim : IDisposable
{
    //Specifying if the lock can be reacquired recursively.
    bool fIsReentrant;
    int myLock;

    //The variables controlling spinning behavior of Mylock(which is a spin-lock)
    const int LockSpinCycles = 20;
    const int LockSpinCount = 10;
    const int LockSleep0Count = 5;

    // These variables allow use to avoid Setting events (which is expensive) if we don't have to. 
    uint numWriteWaiters;        // maximum number of threads that can be doing a WaitOne on the writeEvent 
    uint numReadWaiters;         // maximum number of threads that can be doing a WaitOne on the readEvent
    uint numWriteUpgradeWaiters;      // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1). 
    uint numUpgradeWaiters;

    //Variable used for quick check when there are no waiters.
    bool fNoWaiters;

    int upgradeLockOwnerId;
    int writeLockOwnerId;

    // conditions we wait on. 
    EventWaitHandle writeEvent;    // threads waiting to acquire a write lock go here.
    EventWaitHandle readEvent;     // threads waiting to acquire a read lock go here (will be released in bulk)
    EventWaitHandle upgradeEvent;  // thread waiting to acquire the upgrade lock
    EventWaitHandle waitUpgradeEvent;  // thread waiting to upgrade from the upgrade lock to a write lock go here (at most one)

    // Every lock instance has a unique ID, which is used by ReaderWriterCount to associate itself with the lock
    // without holding a reference to it.
    static long s_nextLockID;
    long lockID;

    // See comments on ReaderWriterCount.
    [ThreadStatic]
    static ReaderWriterCount t_rwc;

    bool fUpgradeThreadHoldingRead;

    private const int MaxSpinCount = 20;

    //The uint, that contains info like if the writer lock is held, num of readers etc.
    uint owners;

    private const uint WRITER_HELD = 0x80000000;
    private const uint WAITING_WRITERS = 0x40000000;
    private const uint WAITING_UPGRADER = 0x20000000;

    private const uint MAX_READER = 0x10000000 - 2;
    private const uint READER_MASK = 0x10000000 - 1;
    private bool fDisposed;
    
    public ReaderWriterLockSlim() : this(LockRecursionPolicy.NoRecursion){}

    public ReaderWriterLockSlim(LockRecursionPolicy recursionPolicy)            
    {
        if (recursionPolicy == LockRecursionPolicy.SupportsRecursion)
        {
            fIsReentrant = true;
        }            
        InitializeThreadCounts();
        fNoWaiters = true;
        lockID = Interlocked.Increment(ref s_nextLockID);
    }
    
    private void InitializeThreadCounts()
    {
        upgradeLockOwnerId = -1;
        writeLockOwnerId = -1;
    }
    
    public void EnterReadLock()
    {
        TryEnterReadLock(-1);
    }
    
    public bool TryEnterReadLock(int millisecondsTimeout)
    {
        return TryEnterReadLock(new TimeoutTracker(millisecondsTimeout));
    }
    
    private bool TryEnterReadLock(TimeoutTracker timeout)
    {
        bool result = false;
        try
        {
            result = TryEnterReadLockCore(timeout);
        }
        finally
        {
        }
        return result;
    }
    
    private bool TryEnterReadLockCore(TimeoutTracker timeout)
    {
        if(fDisposed)
            throw new ObjectDisposedException(null);

        ReaderWriterCount lrwc = null;
        int id = Thread.CurrentThread.ManagedThreadId;

        if (!fIsReentrant)
        {
            if (id == writeLockOwnerId)
            {
                //Check for AW->AR
                throw new LockRecursionException(SR.GetString(SR.LockRecursionException_ReadAfterWriteNotAllowed));
            }
         EnterMyLock(); lrwc = GetThreadRWCount(false);

            //Check if the reader lock is already acquired. Note, we could check the presence of a reader by not allocating rwc (But that would lead to two lookups in the common case. It's better to keep a count in the struucture).
            if (lrwc.readercount > 0)
            {
                ExitMyLock();
                throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveReadNotAllowed));
            }
            else if (id == upgradeLockOwnerId)
            {
                //The upgrade lock is already held.Update the global read counts and exit.

                lrwc.readercount++;
                owners++;
                ExitMyLock();
                return true;
            }
        }
        else
        {
            EnterMyLock();
            lrwc = GetThreadRWCount(false);
            if (lrwc.readercount > 0)
            {
                lrwc.readercount++;
                ExitMyLock();
                return true;
            }
            else if (id == upgradeLockOwnerId)
            {
                //The upgrade lock is already held. Update the global read counts and exit.
                lrwc.readercount++;
                owners++;
                ExitMyLock();
                fUpgradeThreadHoldingRead = true;
                return true;
            }
            else if (id == writeLockOwnerId)
            {
                //The write lock is already held.Update global read counts here,
                lrwc.readercount++;
                owners++;
                ExitMyLock();
                return true;
            }
        }

        bool retVal = true;
        int spincount = 0;

        for (; ; )
        {
            // We can enter a read lock if there are only read-locks have been given out and a writer is not trying to get in.  
            if (owners < MAX_READER)
            {
                // Good case, there is no contention, we are basically done
                owners++;       // Indicate we have another reader
                lrwc.readercount++;
                break;
            }

            if (spincount < MaxSpinCount)
            {
                ExitMyLock();
                if (timeout.IsExpired)
                    return false;
                spincount++;
                SpinWait(spincount);
                EnterMyLock();
                //The per-thread structure may have been recycled as the lock is acquired (due to message pumping), load again.
                if(IsRwHashEntryChanged(lrwc))
                    lrwc = GetThreadRWCount(false);
                continue;
            }

            // Drat, we need to wait.  Mark that we have waiters and wait.  
            if (readEvent == null)      // Create the needed event 
            {
                LazyCreateEvent(ref readEvent, false);
                if (IsRwHashEntryChanged(lrwc))
                    lrwc = GetThreadRWCount(false);
                continue;   // since we left the lock, start over. 
            }

            retVal = WaitOnEvent(readEvent, ref numReadWaiters, timeout);
            if (!retVal)
            {
                return false;
            }
            if (IsRwHashEntryChanged(lrwc))
                lrwc = GetThreadRWCount(false);
        }

        ExitMyLock(); return retVal;
    }
    
    private void EnterMyLock()
    {
        if (Interlocked.CompareExchange(ref myLock, 1, 0) != 0)
            EnterMyLockSpin();
    }

    private void EnterMyLockSpin()
    {
        int pc = Environment.ProcessorCount;
        for (int i = 0; ; i++)
        {
            if (i < LockSpinCount && pc > 1)
            {
                Thread.SpinWait(LockSpinCycles * (i + 1));    // Wait a few dozen instructions to let another processor release lock. 
            }
            else if (i < (LockSpinCount + LockSleep0Count))
            {
                Thread.Sleep(0);        // Give up my quantum.  
            }
            else
            {
                Thread.Sleep(1);        // Give up my quantum.  
            }

            if (myLock == 0 && Interlocked.CompareExchange(ref myLock, 1, 0) == 0)
                return;
        }
    }

    private void ExitMyLock()
    {
        Debug.Assert(myLock != 0, "Exiting spin lock that is not held");
        Volatile.Write(ref myLock, 0);
    }
    /// DontAllocate is set to true if the caller just wants to get an existing entry for this thread, but doesn't want to add one if an existing one could not be found.  
    private ReaderWriterCount GetThreadRWCount(bool dontAllocate)
    {
        ReaderWriterCount rwc = t_rwc;
        ReaderWriterCount empty = null;
        while (rwc != null)
        {
            if (rwc.lockID == this.lockID)
                return rwc;
                
            if (!dontAllocate && empty == null && IsRWEntryEmpty(rwc))
                empty = rwc;

            rwc = rwc.next;
        }
        
        if (dontAllocate)
            return null;

        if (empty == null)
        {
            empty = new ReaderWriterCount();
            empty.next = t_rwc;
            t_rwc = empty;
        }

        empty.lockID = this.lockID;
        return empty;
    }
    
    private static bool IsRWEntryEmpty(ReaderWriterCount rwc)
    {
        if (rwc.lockID == 0)
            return true;
        else if (rwc.readercount == 0 && rwc.writercount == 0 && rwc.upgradecount == 0)
            return true;
        else
            return false;
    }
    
    public void ExitReadLock()
    {
        ReaderWriterCount lrwc = null;
      EnterMyLock(); lrwc = GetThreadRWCount(true);
        if (lrwc == null || lrwc.readercount < 1)
        {
            //You have to be holding the read lock to make this call.
            ExitMyLock();
            throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedRead));
        }

        if (fIsReentrant)
        {
            if (lrwc.readercount > 1)
            {
                lrwc.readercount--;
                ExitMyLock();
                return;
            }

            if (Thread.CurrentThread.ManagedThreadId == upgradeLockOwnerId)
            {
                fUpgradeThreadHoldingRead = false;
            }
        }
        --owners;
        lrwc.readercount--; ExitAndWakeUpAppropriateWaiters();
    }
    
    private void ExitAndWakeUpAppropriateWaiters()
    {
        if (fNoWaiters)
        {
            ExitMyLock(); return;
        }

        ExitAndWakeUpAppropriateWaitersPreferringWriters();
    }
    
    public void EnterWriteLock()
    {
        TryEnterWriteLock(-1);
    }
    public bool TryEnterWriteLock(int millisecondsTimeout)
    {
        return TryEnterWriteLock(new TimeoutTracker(millisecondsTimeout));
    }
    
    private bool TryEnterWriteLock(TimeoutTracker timeout)
    {
        bool result = false;
        try
        {
            result = TryEnterWriteLockCore(timeout);
        }
        finally
        {
        }
        return result;
    }
    
    private bool TryEnterWriteLockCore(TimeoutTracker timeout)
    {
        if(fDisposed)
            throw new ObjectDisposedException(null); 

        int id = Thread.CurrentThread.ManagedThreadId;
        ReaderWriterCount lrwc;
        bool upgradingToWrite = false;

        if (!fIsReentrant)
        {
            if (id == writeLockOwnerId)
            {
                //Check for AW->AW
                throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveWriteNotAllowed));
            }
            else if (id == upgradeLockOwnerId)
            {
                //AU->AW case is allowed once.
                upgradingToWrite = true;
            }

          EnterMyLock(); lrwc = GetThreadRWCount(true);

            //Can't acquire write lock with reader lock held. 
            if (lrwc != null && lrwc.readercount > 0)
            {
                ExitMyLock();
                throw new LockRecursionException(SR.GetString(SR.LockRecursionException_WriteAfterReadNotAllowed));
            }
        }
        else
        {
            EnterMyLock();
            lrwc = GetThreadRWCount(false);
            if (id == writeLockOwnerId)
            {
                lrwc.writercount++;
                ExitMyLock();
                return true;
            }
            else if (id == upgradeLockOwnerId)
            {
                upgradingToWrite = true;
            }
            else if (lrwc.readercount > 0)
            {
                //Write locks may not be acquired if only read locks have been acquired.
                ExitMyLock();
                throw new LockRecursionException(SR.GetString(SR.LockRecursionException_WriteAfterReadNotAllowed));
            }
        }
        int spincount = 0;
        bool retVal = true;
        for (; ; )
        {
            if (IsWriterAcquired())
            {
                // Good case, there is no contention, we are basically done
                SetWriterAcquired();
                break;
            }

            //Check if there is just one upgrader, and no readers.Assumption: Only one thread can have the upgrade lock, so the following check will fail for all other threads that may sneak in when the upgrading thread is waiting.
            
            if (upgradingToWrite)
            {
                uint readercount = GetNumReaders();
                if (readercount == 1)
                {
                    //Good case again, there is just one upgrader, and no readers.
                    SetWriterAcquired();    // indicate we have a writer.
                    break;
                }
                else if (readercount == 2)
                {
                    if (lrwc != null)
                    {
                        if (IsRwHashEntryChanged(lrwc))
                            lrwc = GetThreadRWCount(false);

                        if (lrwc.readercount > 0)
                        {
                            //Good case again, there is just one upgrader, and no readers.
                            SetWriterAcquired();   // indicate we have a writer.
                            break;
                        }                            
                    }
                }
            }

            if (spincount < MaxSpinCount)
            {
                ExitMyLock();
                if (timeout.IsExpired)
                    return false;
                spincount++;
                SpinWait(spincount);
                EnterMyLock();
                continue;
            }

            if (upgradingToWrite)
            {
                if (waitUpgradeEvent == null)   // Create the needed event
                {
                    LazyCreateEvent(ref waitUpgradeEvent, true);
                    continue;   // since we left the lock, start over. 
                }
                retVal = WaitOnEvent(waitUpgradeEvent, ref numWriteUpgradeWaiters, timeout);

                //The lock is not held in case of failure.
                if (!retVal)
                    return false;
            }
            else
            {
                // Drat, we need to wait.  Mark that we have waiters and wait.
                if (writeEvent == null)     // create the needed event.
                {
                    LazyCreateEvent(ref writeEvent, true);
                    continue;   // since we left the lock, start over. 
                }

                retVal = WaitOnEvent(writeEvent, ref numWriteWaiters, timeout);
                //The lock is not held in case of failure.
                if (!retVal)
                    return false;
            }
        }

        if (fIsReentrant)
        {
            if (IsRwHashEntryChanged(lrwc))
                lrwc = GetThreadRWCount(false);
            lrwc.writercount++;
        }

      ExitMyLock(); writeLockOwnerId = id;
        return true;
    }
    
    public void ExitWriteLock()
    {
        ReaderWriterCount lrwc;
        if (!fIsReentrant)
        {
            if (Thread.CurrentThread.ManagedThreadId != writeLockOwnerId)
            {
                //You have to be holding the write lock to make this call.
                throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite));
            }
            EnterMyLock();
        }
        else
        {
            EnterMyLock();
            lrwc = GetThreadRWCount(false);

            if (lrwc == null)
            {
                ExitMyLock();
                throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite));
            }

            if (lrwc.writercount < 1)
            {
                ExitMyLock();
                throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedWrite));
            }

            lrwc.writercount--;

            if (lrwc.writercount > 0)
            {
                ExitMyLock();
                return;
            }
        }

      ClearWriterAcquired(); writeLockOwnerId = -1; ExitAndWakeUpAppropriateWaiters();
    }
    
    public void EnterUpgradeableReadLock()
    {
        TryEnterUpgradeableReadLock(-1);
    }
    
    private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout)
    {
        bool result = false;
        try
        {
            result = TryEnterUpgradeableReadLockCore(timeout);
        }
        finally
        {
        }
        return result;
    }
        
    private bool TryEnterUpgradeableReadLockCore(TimeoutTracker timeout)
    {
        if(fDisposed)
            throw new ObjectDisposedException(null); 

        int id = Thread.CurrentThread.ManagedThreadId;
        ReaderWriterCount lrwc;

        if (!fIsReentrant)
        {
            if (id == upgradeLockOwnerId)
            {
                //Check for AU->AU
                throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveUpgradeNotAllowed));
            }
            else if (id == writeLockOwnerId)
            {
                //Check for AU->AW
                throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterWriteNotAllowed));
            }
            
           EnterMyLock(); lrwc = GetThreadRWCount(true);
            //Can't acquire upgrade lock with reader lock held. 
            if (lrwc != null && lrwc.readercount > 0)
            {
                ExitMyLock();
                throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterReadNotAllowed));
            }
        }
        else
        {
            EnterMyLock();
            lrwc = GetThreadRWCount(false);

            if (id == upgradeLockOwnerId)
            {
                lrwc.upgradecount++;
                ExitMyLock();
                return true;
            }
            else if (id == writeLockOwnerId)
            {
                //Write lock is already held, Just update the global state to show presence of upgrader.
                Debug.Assert((owners & WRITER_HELD) > 0);
                owners++;
                upgradeLockOwnerId = id;
                lrwc.upgradecount++;
                if (lrwc.readercount > 0)
                    fUpgradeThreadHoldingRead = true;
                ExitMyLock();                    
                return true;
            }
            else if (lrwc.readercount > 0)
            {
                //Upgrade locks may not be acquired if only read locks have been acquired.                
                ExitMyLock();
                throw new LockRecursionException(SR.GetString(SR.LockRecursionException_UpgradeAfterReadNotAllowed));
            }
        }

        bool retVal = true;
        int spincount = 0;

        for (; ; )
        {
            //Once an upgrade lock is taken, it's like having a reader lock held
            //until upgrade or downgrade operations are performed.              

            if ((upgradeLockOwnerId == -1) && (owners < MAX_READER))
            {
              owners++;
                upgradeLockOwnerId = id;
                break;
            }

            if (spincount < MaxSpinCount)
            {
                ExitMyLock();
                if (timeout.IsExpired)
                    return false;
                spincount++;
                SpinWait(spincount);
                EnterMyLock();
                continue;
            }

            // Drat, we need to wait.  Mark that we have waiters and wait. 
            if (upgradeEvent == null)   // Create the needed event
            {
                LazyCreateEvent(ref upgradeEvent, true);
                continue;   // since we left the lock, start over. 
            }

            //Only one thread with the upgrade lock held can proceed.
            retVal = WaitOnEvent(upgradeEvent, ref numUpgradeWaiters, timeout);
            if (!retVal)
                return false;
        }

        if (fIsReentrant)
        {
            //The lock may have been dropped getting here, so make a quick check to see whether some other
            //thread did not grab the entry.
            if (IsRwHashEntryChanged(lrwc))
                lrwc = GetThreadRWCount(false);
            lrwc.upgradecount++;
        }

      ExitMyLock(); return true;
    }
    
    public void ExitUpgradeableReadLock()
    {
        ReaderWriterCount lrwc;
        if (!fIsReentrant)
        {
            if (Thread.CurrentThread.ManagedThreadId != upgradeLockOwnerId)
            {
                //You have to be holding the upgrade lock to make this call.
                throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade));
            }
        EnterMyLock();
        }
        else
        {
            EnterMyLock();
            lrwc = GetThreadRWCount(true);

            if (lrwc == null)
            {
                ExitMyLock();
                throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade));
            }

            if (lrwc.upgradecount < 1)
            {
                ExitMyLock();
                throw new SynchronizationLockException(SR.GetString(SR.SynchronizationLockException_MisMatchedUpgrade));
            }

            lrwc.upgradecount--;

            if (lrwc.upgradecount > 0)
            {
                ExitMyLock();
                return;
            }
            fUpgradeThreadHoldingRead = false;
        }

       owners--;
        upgradeLockOwnerId = -1; 
        ExitAndWakeUpAppropriateWaiters();
    }
    
    private void ExitAndWakeUpAppropriateWaitersPreferringWriters()
    {
        bool setUpgradeEvent = false;
        bool setReadEvent = false;
        uint readercount = GetNumReaders();

        //We need this case for EU->ER->EW case, as the read count will be 2 in that scenario.
        if (fIsReentrant)
        {
            if (numWriteUpgradeWaiters > 0 && fUpgradeThreadHoldingRead && readercount == 2)
            {
                ExitMyLock();          // Exit before signaling to improve efficiency (wakee will need the lock)
                waitUpgradeEvent.Set();     // release all upgraders (however there can be at most one). 
                return;
            }
        }

        if (readercount == 1 && numWriteUpgradeWaiters > 0)
        {
            //We have to be careful now, as we are droppping the lock. No new writes should be allowed to sneak in if an upgrade was pending. 
            ExitMyLock();          // Exit before signaling to improve efficiency (wakee will need the lock)
            waitUpgradeEvent.Set();     // release all upgraders (however there can be at most one).            
        }
        else if (readercount == 0 && numWriteWaiters > 0)
        {
            ExitMyLock();      // Exit before signaling to improve efficiency (wakee will need the lock)
            writeEvent.Set();   // release one writer. 
        }
        else if (readercount >= 0)
        {
            if (numReadWaiters != 0 || numUpgradeWaiters != 0)
            {
                if (numReadWaiters != 0)
                    setReadEvent = true;

                if (numUpgradeWaiters != 0 && upgradeLockOwnerId == -1)
                {
                    setUpgradeEvent = true;
                }

                ExitMyLock();    // Exit before signaling to improve efficiency (wakee will need the lock)

                if (setReadEvent)
                    readEvent.Set();  // release all readers. 

                if (setUpgradeEvent)
                    upgradeEvent.Set(); //release one upgrader.
            }
            else
                ExitMyLock();
        }
        else
            ExitMyLock();
    }
    
    private void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent)
    {
        ExitMyLock();
        EventWaitHandle newEvent;
        if (makeAutoResetEvent)
            newEvent = new AutoResetEvent(false);
        else
            newEvent = new ManualResetEvent(false);
        EnterMyLock();
        if (waitEvent == null)          // maybe someone snuck in. 
            waitEvent = newEvent;
        else
            newEvent.Close();
    }
    
    private bool IsRwHashEntryChanged(ReaderWriterCount lrwc)
    {
        return lrwc.lockID != this.lockID;
    }
    
    private bool IsWriterAcquired()
    {
        return (owners & ~WAITING_WRITERS) == 0;
    }    
    private void SetWriterAcquired()
    {
        owners |= WRITER_HELD;    // indicate we have a writer.
    }
    
    private bool WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, TimeoutTracker timeout)
    {
        waitEvent.Reset();
        numWaiters++;
        fNoWaiters = false;

        //Setting these bits will prevent new readers from getting in.
        if (numWriteWaiters == 1)
            SetWritersWaiting();
        if (numWriteUpgradeWaiters == 1)
            SetUpgraderWaiting();            

        bool waitSuccessful = false;
        ExitMyLock();      // Do the wait outside of any lock

        try
        {
            waitSuccessful = waitEvent.WaitOne(timeout.RemainingMilliseconds);
        }
        finally
        {
            EnterMyLock();
            --numWaiters;

            if (numWriteWaiters == 0 && numWriteUpgradeWaiters == 0 && numUpgradeWaiters == 0 && numReadWaiters == 0)
                fNoWaiters = true;

            if (numWriteWaiters == 0)
                ClearWritersWaiting();
            if (numWriteUpgradeWaiters == 0)
                ClearUpgraderWaiting();
            
            if (!waitSuccessful)        // We may also be aboutto throw for some reason.  Exit myLock. 
                ExitMyLock();
        }
        return waitSuccessful;
    }
    
    private uint GetNumReaders()
    {
        return owners & READER_MASK;
    }
    
    public int CurrentReadCount
    {
        get
        {
            int numreaders = (int)GetNumReaders();

            if (upgradeLockOwnerId != -1)
                return numreaders - 1;
            else
                return numreaders;
        }
    }
    private struct TimeoutTracker
    {
        private int m_total;
        private int m_start;
        public TimeoutTracker(TimeSpan timeout)
        {
            long ltm = (long)timeout.TotalMilliseconds;
            if (ltm < -1 || ltm > (long)Int32.MaxValue)
                throw new ArgumentOutOfRangeException("timeout");
            m_total = (int)ltm;
            if (m_total != -1 && m_total != 0)
                m_start = Environment.TickCount;
            else
                m_start = 0;
        }

        public TimeoutTracker(int millisecondsTimeout)
        {
            if (millisecondsTimeout < -1)
                throw new ArgumentOutOfRangeException("millisecondsTimeout");
            m_total = millisecondsTimeout;
            if (m_total != -1 && m_total != 0)
                m_start = Environment.TickCount;
            else
                m_start = 0;
        }

        public int RemainingMilliseconds
        {
            get
            {
                if (m_total == -1 || m_total == 0)
                    return m_total;

                int elapsed = Environment.TickCount - m_start;
                // elapsed may be negative if TickCount has overflowed by 2^31 milliseconds.
                if (elapsed < 0 || elapsed >= m_total)
                    return 0;
                return m_total - elapsed;
            }
        }

        public bool IsExpired
        {
            get
            {
                return RemainingMilliseconds == 0;
            }
        }
    }
}

ReaderWriterLockSlim的默認夠着函數是不支持遞歸的,EnterReadLock的核心方法是TryEnterReadLockCore,TryEnterReadLockCore方法的實現也比較簡單,首先進入本身的鎖,而後獲取本身ReaderWriterCount實例,由於通常狀況下咱們的reader都不會到達上限,因此直接 把ReaderWriterCount的readercount加1,owners++; 而後退出鎖,返回成功,若是是遞歸調用就要複雜一點了。ExitReadLock也是很是簡單,同樣是進入本身的鎖,找到ReaderWriterCount實例,執行--owners;lrwc.readercount--; 在退出本身的鎖,到這裏咱們知道共享鎖的一個實現實例。ui

EnterWriteLock 的實現核心是TryEnterWriteLockCore方法,TryEnterWriteLockCore方法由於只有一個線程能夠進入,因此有點不同,不考慮遞歸也是先進入鎖的模式,查找ReaderWriterCount實例,而後執行if (IsWriterAcquired()){SetWriterAcquired();break;}【當前申請讀鎖成功,通常是沒有這個好的狀況的哦】,實際更多狀況都會執行到retVal = WaitOnEvent(writeEvent, ref numWriteWaiters, timeout);等待鎖,最後記錄鎖的id ,writeLockOwnerId = id; ExitWriteLock的實現 writeLockOwnerId = -1,而後在ExitAndWakeUpAppropriateWaitersthis

EnterUpgradeableReadLock的實現核心是TryEnterUpgradeableReadLockCore方法,也是進入鎖模式,獲取ReaderWriterCount實例,若是當前沒有升級鎖則 執行【owners++;upgradeLockOwnerId = id;】,否者就只有等待了retVal = WaitOnEvent(upgradeEvent, ref numUpgradeWaiters, timeout);  ExitUpgradeableReadLock的實現【 owners--;upgradeLockOwnerId = -1;】最後調用ExitAndWakeUpAppropriateWaiters, ExitAndWakeUpAppropriateWaiters這個方法就是檢查是否有鎖與須要重置信號。spa

 在這裏也實現了不用lock來實現lock的效果:線程

  int myLock;
  private void EnterMyLock()
    {
        if (Interlocked.CompareExchange(ref myLock, 1, 0) != 0)
            EnterMyLockSpin();
    }

    private void EnterMyLockSpin()
    {
        int pc = Environment.ProcessorCount;
        for (int i = 0; ; i++)
        {
            if (i < LockSpinCount && pc > 1)
            {
                Thread.SpinWait(LockSpinCycles * (i + 1));    // Wait a few dozen instructions to let another processor release lock. 
            }
            else if (i < (LockSpinCount + LockSleep0Count))
            {
                Thread.Sleep(0);        // Give up my quantum.  
            }
            else
            {
                Thread.Sleep(1);        // Give up my quantum.  
            }

            if (myLock == 0 && Interlocked.CompareExchange(ref myLock, 1, 0) == 0)
                return;
        }
    }
    private void ExitMyLock()
    {
        Debug.Assert(myLock != 0, "Exiting spin lock that is not held");
        Volatile.Write(ref myLock, 0);
    }
相關文章
相關標籤/搜索