{C#} ReaderWriterLockSlim 원본 읽기

6080 단어
ReaderWriterLockSlim은 ReaderLock 또는 WriterLock으로 귀속되는 것을 지원하지만 아래의 분석은 이러한 상황과 관련이 없습니다.

어떻게 자물쇠를 읽는지 서로 배척하지 않는다

TryEnterReadLockCore(TimeoutTracker timeout)
{
            .......
            // step1
            EnterMyLock();
            lrwc = GetThreadRWCount(false);
            
            if (lrwc.readercount > 0)
            {
                ExitMyLock();
                throw new LockRecursionException(SR.GetString(SR.LockRecursionException_RecursiveReadNotAllowed));
            }

            .......

        // step 2
        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.  
            // 2.1
            if (owners < MAX_READER)
            {
                // Good case, there is no contention, we are basically done
                owners++;       // Indicate we have another reader
                lrwc.readercount++;
                break;
            }

            // 2.2
            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;
            }

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

        // step 3
        ExitMyLock();
}

1단계: 경량 자물쇠 진입
    private void EnterMyLock()
    {
        if (Interlocked.CompareExchange(ref myLock, 1, 0) != 0)
            EnterMyLockSpin();
    }

이 자물쇠는 일반적인 락이 아니다.이것은 원자 조작을 통해 int 형식lock을 설정합니다. 하나의 라인만 설정할 수 있고, 다른 라인은 자전거와 짧은 sleep를 통해 다음 설정이 적용되기를 기다립니다.다른 라인은 EnterReadLock이든 EnterWriteLock이든 ExitMyLock이 호출될 때까지 여기에 표시됩니다.이후 읽기와 쓰기 계수 GetThreadRWCount를 가져옵니다. 각 스레드가 자신의 ReaderWriterCount를 유지하고 한 스레드가 여러 ReaderWriterLockSlim을 사용한다면 목록이 형성됩니다.목록에서 RWC를 회수할 수 있습니다.ReaderLock에 반복적으로 들어가는 것을 방지하기 위해 독자가 있으면 이상이 발생합니다.
2단계: Reader 등록
만약 owners가 최대치를 초과하지 않았다면, 이것은 가장 좋은 상황입니다. 여러 개의 reader가 등록할 수 있습니다.이곳은 진정으로 독자들이 서로 배척하지 않는 것을 구현하였다.여러 스레드가 EnterReadLock을 호출하면 후속 코드가 원활하게 실행됩니다.(mylock을 방출한 후 owners가 제한을 초과하지 않으면 다른 라인도 이 코드에 들어가서 막히지 않습니다)
만약에 owners가 최대치를 초과하면(단순히 reader만 증가하면 발생할 수 없음), 여기서 가장 큰 것은 이전의 다른 라인이 EnterWriteLock되었을 것입니다. 다음에 우리는 Try EnterWriteLockCore에서 볼 수 있습니다. writer는 owners를 초과값으로 설정하여 독자 등록을 막고 2.2로 넘어갑니다.
등록에 실패하면 먼저 자전거를 시도합니다. 자전거 시간 내에 writer가 lock을 방출하면 독자를 다시 등록합니다.
N번 자선을 시도한 후에도 성공하지 못하면reader Event(ManualReset Event)를 만들고 기다려야 합니다.writer가 종료 상태를 터치할 때, 모든 기다린reader가 알림을 받습니다.
3단계: 경량 자물쇠 방출
    private void ExitMyLock()
    {
        Volatile.Write(ref myLock, 0);
    }

간단합니다. intlock을 0으로 설정하면 다른 기다리는 라인이 mylock을 성공적으로 설정할 수 있습니다.

어떻게 읽고 쓰며 서로 배척합니까


이전에 쓰기는 owners를 통해 배척하고 쓰기를 배척하는 방법을 살펴보았다.
    private bool TryEnterWriteLockCore(TimeoutTracker timeout)
    {
        ......

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

        ......

        for (; ; )
        {
            if (IsWriterAcquired())
            {
                // Good case, there is no contention, we are basically done
                SetWriterAcquired();
                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 (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;

        }

        ExitMyLock();

        writeLockOwnerId = id;

        return true;
    }

자물쇠를 얻는 것은 더 이상 말할 필요가 없다.이후의 조작을 보고 먼저 표지 위치를 설정한다.
    private bool IsWriterAcquired()
    {
        return (owners & ~WAITING_WRITERS) == 0;
    }

reader가 등록되어 있다면, 여기서false로 돌아가면, writer는 후속 대기에 들어가고, 그렇지 않으면 로고 위치를 설정합니다.
    private void SetWriterAcquired()
    {
        owners |= WRITER_HELD;    // indicate we have a writer.
    }

이 로고 위치를 설정하면reader를 밖으로 차단합니다.
후속 대기 절차가 많지 않습니다. 여기서 만든 writer Event는 AutoReset Event입니다. 이것은 한 번에 하나의 writer만 깨울 수 있음을 의미합니다.이것은 작가 간의 경쟁을 피했다.

이벤트 알림 시간


reader Event는 ExistWriteLock, writerEvent는 ExistReadLock에 있습니다.이것도 과학적이다.

포장기를 하나 쓰다


실현하기 어렵지 않습니다. using을 지원하면 됩니다.

좋은 웹페이지 즐겨찾기