深入理解.Net中的线程同步之构造模式(二)内核模式3.内核模式构造物Mutex

138 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

深入理解.Net中的线程同步之构造模式(二)内核模式构造

@TOC

前言

Kernel-mode Constructs一般翻译为内核模式构造 , Constructs 即是一个名词也是一个动词,但是翻译为构造感觉有点像一个动词,个人感觉翻译为名词更好,毕竟是加了s的,就表示多个,动词显然不能表示多个啊,比如内核模式构造物,内核模式构造体。


环境说明 IDE:Visual Studio 2022 OS:Win10 .NET:.Net4.6.1

一、互斥体是什么?

互斥体mutex就是一个互斥的锁,和AutoResetEvent 事件锁类似。 只不过mutex比AutoResetEvent 事件锁多了一些额外的功能。mutex支持同一个线程多次获取一个锁,当然,多次获取后也需要多次释放才能让其他的线程可用,我们将这个多次获得锁的过程叫做递归锁,也叫可重入锁。但是使用mutex的时候,会频繁切换内核代码和托管代码,这样对程序的性能有一定的影响。可以用AutoRestEvent方法自己实现一个递归锁代替。 但是确实没有在编码中用到递归锁,也不能很好的想出一个场景。 难不成递归的时候用递归锁?好像也可以说的通喔!

二、代码编写

1.编写一个基于AutoRestEvent的递归锁

主要是用来理解一下递归锁的实现,相对于普通的事件锁,主要增加了2个变量来实现,一个是记录获得锁的线程ID变量,一个是记录获取锁次数的变量。

代码如下(示例):

    internal sealed class RecursiveAutoResetEvent : IDisposable
    {
        private AutoResetEvent m_lock = new AutoResetEvent(true);
        private Int32 m_owningThreadId = 0;
        private Int32 m_recursionCount = 0;

        public void Enter()
        {
            // Obtain the calling thread's unique Int32 ID
            // 获取唯一的线程ID
            Int32 currentThreadId = Thread.CurrentThread.ManagedThreadId;

            // If the calling thread owns the lock, increment the recursion count
            // 
            if (m_owningThreadId == currentThreadId)
            {
                m_recursionCount++;
                return;
            }

            // The calling thread doesn't own the lock, wait for it
            m_lock.WaitOne();

            // The calling now owns the lock, initialize the owning thread ID & recursion count
            m_owningThreadId = currentThreadId;
            m_recursionCount--;
        }

        public void Leave()
        {
            // If the calling thread doesn't own the lock, we have an error
            if (m_owningThreadId != Thread.CurrentThread.ManagedThreadId)
                throw new InvalidOperationException();

            // Subtract 1 from the recursion count
            if (--m_recursionCount == 0)
            {
                // If the recursion count is 0, then no thread owns the lock
                m_owningThreadId = 0;
                m_lock.Set();   // Wake up 1 waiting thread (if any)
            }
        }

        public void Dispose() { m_lock.Dispose(); }
    }   

总结

递归锁我们使用的情况很少,而且mutex的递归锁存在多次从托管代码转到内核代码,性能理论上要低于AutoResetEvent的递归锁。