ReaderWriterLockSlim and async\await

前端 未结 4 1025
自闭症患者
自闭症患者 2020-12-09 14:40

I have some problems with ReaderWriterLockSlim. I cannot understand how it\'s magic working.

My code:

 private async Task LoadIndex()
           


        
相关标签:
4条回答
  • 2020-12-09 15:32

    You can safely emulate a reader/writer locking mechanism using the reliable and lightweight SemaphoreSlim and keep the benefits of async/await. Create the SemaphoreSlim giving it the number of available locks equivalent to the number of routines that will lock your resource for reading simultaneously. Each one will request one lock as usual. For your writing routine, make sure it requests all the available locks before doing its thing.

    That way, your writing routine will always run alone while your reading routines might share the resource only between themselves.

    For example, suppose you have 2 reading routines and 1 writing routine.

    SemaphoreSlim semaphore = new SemaphoreSlim(2);
    
    async void Reader1()
    {
        await semaphore.WaitAsync();
        try
        {
            // ... reading stuff ...
        }
        finally
        {
            semaphore.Release();
        }
    }
    
    async void Reader2()
    {
        await semaphore.WaitAsync();
        try
        {
            // ... reading other stuff ...
        }
        finally
        {
            semaphore.Release();
        }
    }
    
    async void ExclusiveWriter()
    {
        // the exclusive writer must request all locks
        // to make sure the readers don't have any of them
        // (I wish we could specify the number of locks
        // instead of spamming multiple calls!)
        await semaphore.WaitAsync();
        await semaphore.WaitAsync();
        try
        {
            // ... writing stuff ...
        }
        finally
        {
            // release all locks here
            semaphore.Release(2);
            // (oh here we don't need multiple calls, how about that)
        }
    }
    

    Obviously this method only works if you know beforehand how many reading routines you could have running at the same time. Admittedly, too much of them would make this code very ugly.

    0 讨论(0)
  • 2020-12-09 15:33

    ReaderWriterLockSlim is a thread-affine lock type, so it usually cannot be used with async and await.

    You should either use SemaphoreSlim with WaitAsync, or (if you really need a reader/writer lock), use my AsyncReaderWriterLock from AsyncEx or Stephen Toub's AsyncReaderWriterLock.

    0 讨论(0)
  • 2020-12-09 15:34

    Like Stephen Cleary says, ReaderWriterLockSlim is a thread-affine lock type, so it usually cannot be used with async and await.


    You have to build a mechanism to avoid readers and writers accessing shared data at the same time. This algorithm should follow a few rules.

    When requesting a readerlock:

    • Is there a writerlock active?
    • Is there anything already queued? (I think it's nice to execute it in order of requests)

    When requesting a writerlock:

    • Is there a writerlock active? (because writerlocks shouldn't execute parallel)
    • Are there any reader locks active?
    • Is there anything already queued?

    If any of these creteria answers yes, the execution should be queued and executed later. You could use TaskCompletionSources to continue awaits.

    When any reader or writer is done, You should evaluate the queue and continue execute items when possible.


    For example (nuget): AsyncReaderWriterLock

    0 讨论(0)
  • 2020-12-09 15:39

    Some time ago I implemented for my project class AsyncReaderWriterLock based on two SemaphoreSlim. Hope it can help. It is implemented the same logic (Multiple Readers and Single Writer) and at the same time support async/await pattern. Definitely, it does not support recursion and has no protection from incorrect usage:

    var rwLock = new AsyncReaderWriterLock();
    
    await rwLock.AcquireReaderLock();
    try
    {
        // ... reading ...
    }
    finally
    {
        rwLock.ReleaseReaderLock();
    }
    
    await rwLock.AcquireWriterLock();
    try
    {
        // ... writing ...
    }
    finally
    {
        rwLock.ReleaseWriterLock();
    }
    
    
    
    public sealed class AsyncReaderWriterLock : IDisposable
    {
        private readonly SemaphoreSlim _readSemaphore  = new SemaphoreSlim(1, 1);
        private readonly SemaphoreSlim _writeSemaphore = new SemaphoreSlim(1, 1);
        private          int           _readerCount;
    
        public async Task AcquireWriterLock(CancellationToken token = default)
        {
            await _writeSemaphore.WaitAsync(token).ConfigureAwait(false);
            await SafeAcquireReadSemaphore(token).ConfigureAwait(false);
        }
    
        public void ReleaseWriterLock()
        {
            _readSemaphore.Release();
            _writeSemaphore.Release();
        }
    
        public async Task AcquireReaderLock(CancellationToken token = default)
        {
            await _writeSemaphore.WaitAsync(token).ConfigureAwait(false);
    
            if (Interlocked.Increment(ref _readerCount) == 1)
            {
                await SafeAcquireReadSemaphore(token).ConfigureAwait(false);
            }
    
            _writeSemaphore.Release();
        }
    
        public void ReleaseReaderLock()
        {
            if (Interlocked.Decrement(ref _readerCount) == 0)
            {
                _readSemaphore.Release();
            }
        }
    
        private async Task SafeAcquireReadSemaphore(CancellationToken token)
        {
            try
            {
                await _readSemaphore.WaitAsync(token).ConfigureAwait(false);
            }
            catch
            {
                _writeSemaphore.Release();
    
                throw;
            }
        }
    
        public void Dispose()
        {
            _writeSemaphore.Dispose();
            _readSemaphore.Dispose();
        }
    }
    
    0 讨论(0)
提交回复
热议问题