多线程编程学习系列之---Monitor类

先看一段代码:
class Program
{
    static void Main(string[] args)
    {
        //开启两个线程
        var threadOne = new Thread(new ThreadSample("ThreadOne").CountNumbersByLock);
        threadOne.Start();
        var threadTwo = new Thread(new ThreadSample("ThreadTwo").CountNumbersByLock);
        threadTwo.Start();

        //var threadOne = new Thread(new ThreadSample("ThreadOne").CountNumbersByMonitor);
        //threadOne.Start();
        //var threadTwo = new Thread(new ThreadSample("ThreadTwo").CountNumbersByMonitor);
        //threadTwo.Start();
        Console.ReadKey();
    }
}

class ThreadSample
{
    private readonly string _threadNo;
    private readonly static object staticObject = new object();

    public ThreadSample(string threadNo)
    {
        _threadNo = threadNo;
    }

    public void CountNumbersByLock()
    {
        Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
        lock (staticObject)
        {
            Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine($"线程 {_threadNo} is count number..... ");
            
        }
        Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
    }

    public void CountNumbersByMonitor()
    {
        bool isRequiredLock = false;
        try
        {
            Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
            Monitor.Enter(staticObject, ref isRequiredLock);
            Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine($"线程 {_threadNo} is count number..... ");
        }
        finally
        {
            if (isRequiredLock)
            {
                Monitor.Exit(staticObject);
                Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
            }
        }
        
    }
}
调用CountNumbersByLock结果:

image

调用CountNumbersByMonitor结果:

image

结果一样,为什么呢?实际上lock关键字是Monitor类用例的一个语法糖。lock的效果等同于
bool isRequiredLock = false;
try
{
    
    Monitor.Enter(staticObject, ref isRequiredLock);
    // code
}
finally
{
    if (isRequiredLock)
    {
        Monitor.Exit(staticObject);
        // code
    }
}
接下来看一个死锁的例子:
class Program
{
    static void Main(string[] args)
    {
        //开启两个线程
        var threadOne = new Thread(new ThreadSample("ThreadOne").LockA);
        threadOne.Start();
        var threadTwo = new Thread(new ThreadSample("ThreadTwo").LockB);
        threadTwo.Start();
        
        Console.ReadKey();
    }
}

class ThreadSample
{
    private readonly string _threadNo;
    private readonly static object staticObjectA = new object();
    private readonly static object staticObjectB = new object();

    public ThreadSample(string threadNo)
    {
        _threadNo = threadNo;
    }

    public void LockA()
    {
        Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
        lock (staticObjectA)
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
            lock (staticObjectB)
            {
                Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine($"线程 {_threadNo} is count number..... ");
            }
        }
        Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
    }
    public void LockB()
    {
        Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
        lock (staticObjectB)
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
            lock (staticObjectA)
            {
                Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine($"线程 {_threadNo} is count number..... ");
            }
        }
        Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
    }

}
结果:

image

线程1和2一直处于等待中形成了死锁。
Monitor类中的TryEnter方法可以设置过期时间,避免死锁问题ThreadSample类修改代码如下:
class ThreadSample
{
    private readonly string _threadNo;
    private readonly static object staticObjectA = new object();
    private readonly static object staticObjectB = new object();

    public ThreadSample(string threadNo)
    {
        _threadNo = threadNo;
    }

    public void LockA()
    {
        Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
        if(Monitor.TryEnter(staticObjectA,TimeSpan.FromSeconds(3)))
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
            if (Monitor.TryEnter(staticObjectB, TimeSpan.FromSeconds(3)))
            {
                Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine($"线程 {_threadNo} is count number..... ");
            }
        }
        Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
    }
    public void LockB()
    {
        Console.WriteLine($"线程 {_threadNo} 等待锁..... ");
        if (Monitor.TryEnter(staticObjectB, TimeSpan.FromSeconds(3)))
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
            if (Monitor.TryEnter(staticObjectA, TimeSpan.FromSeconds(3)))
            {
                Console.WriteLine($"线程 {_threadNo} 获取锁..... ");
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine($"线程 {_threadNo} is count number..... ");
            }
        }
        Console.WriteLine($"线程 {_threadNo} 释放锁..... ");
    }

}
调试结果:

image

上面的代码设置了锁定超时时间为3秒,也就是说,在3秒中后,lockObj还未被解锁,TryEntry方法就会返回false,如果在3秒之内,lockObj被解锁,TryEntry返回true。我们可以使用这种方法来避免死锁

总结:lock的功能就相当于直接调用Monitor的Entry方法,所不同的是,lock方法在结束后,会自动解除锁定, 而Monitor类,就必须调用Exit方法来显式地解除锁定,如果只使用lock语句本身的功能,最好还是直接用lock语句。Monitor类不仅可以完全取代lock语句(),还可以使用TryEntry方法设置一个锁定超时.

发布了37 篇原创文章 · 获赞 3 · 访问量 6334

猜你喜欢

转载自blog.csdn.net/huan13479195089/article/details/88819122