C# 異步鎖【轉】


原文:http://www.yalongyang.com/2013/01/c-sharp-await-lock/

在C#中,普通用鎖很簡單

    object m_lock = new object();
    lock(m_lock)
    {
        ......
    }

其中 ...... 表示互斥的代碼。
這樣就可以保證同時僅會有一個地方在執行這段互斥代碼。

然而如果互斥代碼中由await調用,上面的方式就行不通了,由於普通的lock代碼段中無法存在await調用。

但是在實際使用中,經常遇見需要保護互斥的await情況,
比如用 await FileIO.WriteTextAsync() 的調用寫文件,需要保證同時僅有一個地方在調用此段代碼,不然就會出現互斥錯誤。

以下兩篇文章很好的說明了如何實現對await調用互斥的處理。

可以總結為以下代碼

    class AsyncSemaphore
    {
        private readonly static Task s_completed = Task.FromResult(true);
        private readonly Queue<TaskCompletionSource<bool>> m_waiters = new Queue<TaskCompletionSource<bool>>();
        private int m_currentCount;

        public AsyncSemaphore(int initialCount)
        {
            if (initialCount < 0) throw new ArgumentOutOfRangeException("initialCount");
            m_currentCount = initialCount;
        }

        public Task WaitAsync()
        {
            lock (m_waiters)
            {
                if (m_currentCount > 0)
                {
                    --m_currentCount;
                    return s_completed;
                }
                else
                {
                    var waiter = new TaskCompletionSource<bool>();
                    m_waiters.Enqueue(waiter);
                    return waiter.Task;
                }
            }
        }

        public void Release()
        {
            TaskCompletionSource<bool> toRelease = null;
            lock (m_waiters)
            {
                if (m_waiters.Count > 0)
                    toRelease = m_waiters.Dequeue();
                else
                    ++m_currentCount;
            }
            if (toRelease != null)
                toRelease.SetResult(true);
        }
    }

    public class AsyncLock
    {
        private readonly AsyncSemaphore m_semaphore;
        private readonly Task<Releaser> m_releaser;

        public AsyncLock()
        {
            m_semaphore = new AsyncSemaphore(1);
            m_releaser = Task.FromResult(new Releaser(this)); 
        }

        public Task<Releaser> LockAsync()
        {
            var wait = m_semaphore.WaitAsync();
            return wait.IsCompleted ?
                m_releaser :
                wait.ContinueWith((_, state) => new Releaser((AsyncLock)state),
                    this, CancellationToken.None,
                    TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); 
        }

        public struct Releaser : IDisposable
        {
            private readonly AsyncLock m_toRelease;

            internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; }

            public void Dispose()
            {
                if (m_toRelease != null)
                    m_toRelease.m_semaphore.Release();
            } 
        }
    }

調用時僅需要:

    readonly AsyncLock m_lock = new AsyncLock(); 
    using (var releaser = await m_lock.LockAsync())
    {
        await FileIO.WriteTextAsync(configureFile, jsonString);
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM