C# 自实现非排它锁

前段时间遇到一个需求,我需要一个可以阻塞线程又可以被任意线程解锁的锁。

它需要有如下几个特性:

  1. 可随时读取上锁状态
  2. 无所属线程(非排它)
  3. 可阻塞线程(可以使上锁者陷入阻塞、等待)
  4. 任意线程可随时解锁

微软貌似并没有去搞这些东东出来用,那我就自己瞎搞出来一个:

using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace ConsoleApplication1
{
    /// <summary>
    /// 1.可随时读取上锁状态
    /// 2.无所属线程(非排它)
    /// 3.可阻塞线程(可以使上锁者陷入阻塞、等待)
    /// 4.任意线程可随时解锁
    /// </summary>
    public class SimpleStateLock
    {
        private Thread _aliveWithThread = null;
        private Thread _aliveWithThreadMonitor = null;

        /// <summary>
        /// 是否到了要销毁 SimpleStateLock 对象的时候了
        /// </summary>
        private bool _isDestory = false;

        /// <summary>
        /// 唤醒 服务员 的重要媒介
        /// </summary>
        private object _handlerLock = new object();

        /// <summary>
        /// 服务员 的 任务清单
        /// </summary>
        private Queue<Pipe> _actionQueue = new Queue<Pipe>();

        /// <summary>
        /// 实现类似管道的东西,一端发号施令陷入等待(睡眠),
        /// 另一端完成指令,通知(唤醒)另一端。
        /// </summary>
        private class Pipe
        {
            public Pipe(LockAction lockAction)
            {
                _lockAction = lockAction;
            }

            /// <summary>
            /// 具体任务
            /// </summary>
            private LockAction _lockAction;

            /// <summary>
            /// 一方面提供阻塞、唤醒功能,
            /// 另一方面保证了 _isDone 不存在脏读脏写
            /// </summary>
            private object _birge = new object();

            /// <summary>
            /// 任务是否受理
            /// </summary>
            private bool _isDone = false;

            public LockAction TheAction => _lockAction;
            public bool IsDone => _isDone;


            public void Wait()
            {
                Monitor.Enter(_birge);
                if (_isDone)
                {
                    Monitor.Exit(_birge);
                    return;
                }

                Monitor.Wait(_birge);
                Monitor.Exit(_birge);
            }

            public void Notify()
            {
                Monitor.Enter(_birge);
                _isDone = true;
                Monitor.Pulse(_birge);
                Monitor.Exit(_birge);
            }
        }

        private enum LockAction
        {
            Lock,
            UnLock,
            Nothing
        }

        private object _lockFunctionLock = new object();
        private object _unlockFunctionLock = new object();
        private ReaderWriterLockSlim _theLock = new ReaderWriterLockSlim();

        public bool IsLocked => _theLock.CurrentReadCount > 0;

        public void Lock()
        {
            //确保只有一个上锁者,防止被重复上锁
            Monitor.Enter(_lockFunctionLock);
            if (_isDestory)
            {
                Monitor.Exit(_lockFunctionLock);
                return;
            }
            // Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " Start to do.");

            //如果已锁
            if (IsLocked)
            {
                // Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " Waiting.");
                //陷入等待
                _theLock.EnterWriteLock();
                //只要 “读锁” 被解,那么 “写锁” 必然可以占用
                //如果上面一行成功执行,则“读锁”必然已解锁
                _theLock.ExitWriteLock();
                // Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " Wake up.");
            }

            //上锁
            Pipe pipe = new Pipe(LockAction.Lock);
            _actionQueue.Enqueue(pipe);
            //唤醒 服务员
            WakeUp();
            //等待 服务员 完成任务的好消息
            pipe.Wait();

            // Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " All done.");
            Monitor.Exit(_lockFunctionLock);
        }

        public bool TryLock()
        {
            bool result = false;
            //确保只有一个上锁者,防止被重复上锁
            Monitor.Enter(_lockFunctionLock);
            if (_isDestory)
            {
                Monitor.Exit(_lockFunctionLock);
                return false;
            }

            //如果没有被锁上
            if (!IsLocked)
            {
                //上锁
                Pipe pipe = new Pipe(LockAction.Lock);
                _actionQueue.Enqueue(pipe);
                //唤醒 服务员
                WakeUp();
                //等待 服务员 完成任务的好消息
                pipe.Wait();
                result = IsLocked;
            }

            Monitor.Exit(_lockFunctionLock);
            return result;
        }

        /// <summary>
        /// 解锁
        /// </summary>
        public void UnLock()
        {
            Monitor.Enter(_unlockFunctionLock);

            if (_isDestory)
            {
                Monitor.Exit(_unlockFunctionLock);
                return;
            }

            if (IsLocked)
            {
                Pipe pipe = new Pipe(LockAction.UnLock);
                _actionQueue.Enqueue(pipe);
                //唤醒 服务员
                WakeUp();
                //等待 服务员 完成任务的好消息
                pipe.Wait();
            }

            Monitor.Exit(_unlockFunctionLock);
        }

        public void UnLockThenFree()
        {
            UnLock();
            Free();
        }

        /// <summary>
        /// 一波操作叫醒 服务员,让 服务员 干活
        /// </summary>
        private void WakeUp()
        {
            Monitor.Enter(_handlerLock);
            Monitor.Pulse(_handlerLock);
            Monitor.Exit(_handlerLock);
        }

        /// <summary>
        /// 服务员 处理任务
        /// </summary>
        private void Handle()
        {
            Monitor.Enter(_handlerLock);
            while (true)
            {
                //如果任务都完成了,服务员 就可以摸鱼一会儿了
                if (0 == _actionQueue.Count)
                {
                    Monitor.Wait(_handlerLock);
                    Monitor.Enter(_handlerLock);
                    Monitor.Exit(_handlerLock);
                }

                if (_isDestory)
                {
                    if (Monitor.IsEntered(_handlerLock))
                    {
                        Monitor.Exit(_handlerLock);
                    }
                    break;
                }

                Pipe pipe = _actionQueue.Dequeue();

                if (LockAction.Lock == pipe.TheAction)
                {
                    if (!IsLocked)
                    {
                        _theLock?.EnterReadLock();
                        // Debug.WriteLine("Lock succeed.");
                    }
                    else
                    {
                        // Debug.WriteLine("No need to lock.");
                    }
                }
                else if (LockAction.UnLock == pipe.TheAction)
                {
                    if (IsLocked)
                    {
                        _theLock?.ExitReadLock();
                    }
                }

                //任务完成,通知一下
                pipe.Notify();
            }
        }

        /// <summary>
        /// 为了可以自动释放该对象,增设该函数
        /// </summary>
        /// <param name="thread"></param>
        public void SetAliveWithThread(Thread thread)
        {
            _aliveWithThread = thread;
            if (null == _aliveWithThreadMonitor)
            {
                _aliveWithThreadMonitor = new Thread(() =>
                {
                    while (true)
                    {
                        if (!_aliveWithThread.IsAlive)
                        {
                            Free();
                            Debug.WriteLine(
                                nameof(SimpleStateLock)
                                + "." + nameof(SetAliveWithThread)
                                + ":" + "thread="
                                + _aliveWithThread.ManagedThreadId
                                + ",exit.");
                            break;
                        }

                        //每隔 1 秒检查一下线程情况
                        Thread.Sleep(1000);
                    }
                });
                _aliveWithThreadMonitor.Start();
            }
        }

        public void Destory() => Free();

        public void Release() => Free();

        /// <summary>
        /// 欲使得GC可以释放该对象的内存占用,调用此函数。
        /// </summary>
        public void Free()
        {
            _isDestory = true;
            _actionQueue.Clear();
            WakeUp();
            _theLock.Dispose();
        }

        /// <summary>
        /// 析构函数,完整地释放这个对象所占用的内存。
        /// 实际上这个根本没有什么卵用,
        /// 因为有线程在死循环,RunTime永远不会执行这个析构函数
        /// </summary>
        ~SimpleStateLock()
        {
            Free();
        }

        public SimpleStateLock(bool isLocked = false)
        {
            //服务员上岗
            new Thread(() => { Handle(); }).Start();
            if (isLocked)
            {
                Lock();
            }
        }
    }
}

这个……自己慢慢看吧,原理不想写了。

为什么要折腾这种锁?

因为有一种东西,叫做“事件”和“回调”。
一般情况下,为避免UI卡死,我们会在按钮的回调函数里新起线程,保证后台逻辑不会阻塞UI。
当你希望在UI上点击某个按钮之后,给状态锁上锁,然后后续线程上锁时可以陷入阻塞(线程:我就赖着不走了,多少时间我都愿意等!)。再在UI上点击某个按钮之后,给状态锁解锁,让等待上锁的线程的其中一个继续执行。你会发现做不到。
因为Monitor这个东西,是认主的,它只认线程。假如A线程上锁,B线程解锁,Monitor会抛出异常,说这个锁的拥有者是A不是B,B无权限解锁,Monitor是一种排它锁。

那有没有非排它锁呢?

不知道有没有,反正当我花了3小时面向谷歌搜索都没找到就放弃了。找还不如造呢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值