zoukankan      html  css  js  c++  java
  • C# 线程手册 第三章 使用线程 Monitor.Enter() 和 Monitor.Exit()

    Monitor 方法是静态的,不需要生成Monitor 类的实例就可以直接调用它们。在.NET Framework 中,每个对象都有一个与之关联的锁,对象可以得到并释放它以便于在任意时间只有一个线程可以访问对象实例变量和方法。类似的,.NET Framework 中的每个对象都提供一个允许自己进入等待状态的机制。与锁的机制类似,这种机制的主要目的是为了实现线程间通信。当一个线程进入到一个对象的关键部分且需要一个特定条件并假设另外一个线程将会在同样的关键区域中创建条件时这种机制才会发生。

      现在比较特别的是在任意时间任意关键区域内仅允许有一个线程,而且当第一个线程进入到关键区域以后,其他线程都不能进入。现在问题来了,由于第一个线程已经独占了关键区域,那么第二个线程如何在关键区域内创建一个条件呢? 举个简单例子来说明如何解决这个问题的:如果线程A需要从数据库中获取一些数据,线程B等待所有数据被接收以后处理这些数据,线程B调用Wait()方法来等待线程A在数据到来时通知它。当数据到来以后,线程A会调用Pulse() 方法来通知线程B数据已经到达现在可以处理数据了。这是通过“等待&通知”机制实现的。第一个线程进入关键区域并执行Wait()方法。Wait()方法将锁释放后进入等待状态,然后第二个线程现在可以进入关键区域,改变相关条件,最后调用Pulse() 方法来通知等待线程条件已经满足,现在可以继续执行了。第一个线程再次获得锁,然后从Monitor.Wait() 方法返回并从Monitor.Wait()方法处继续执行。

    任意两个线程都不可以同时进入Enter()方法。这与ATM 的例子类似:任意时间只允许一个人操作账户,其他人不可以操作直到第一个人离开。Monitor.Enter() 和 Monitor.Exit() 方法的命名是很贴近生活的。图2 描述了Monitor 类的工作机制。

    2012-2-3 15-29-15

    图2

    现在我们来看一个使用Enter() 和 Exit() 方法的例子,MonitorEnterExit.cs:

    /*************************************
    /* Copyright (c) 2012 Daniel Dong
     * 
     * Author:oDaniel Dong
     * Blog:o  www.cnblogs.com/danielWise
     * Email:o guofoo@163.com
     * 
     */
    
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    
    namespace MonitorEnterExit
    {
        public class EnterExit
        {
            private int result = 0;
    
            public void NonCriticalSection()
            {
                Console.WriteLine("Entered Thread "
                    + Thread.CurrentThread.GetHashCode());
    
                for (int i = 1; i <= 5; i++)
                {
                    Console.WriteLine("Result = " + result++
                        + " ThreadID " 
                        + Thread.CurrentThread.GetHashCode());
                    Thread.Sleep(1000);
                }
                Console.WriteLine("Exiting Thread "
                    + Thread.CurrentThread.GetHashCode());
            }
    
            public void CriticalSection()
            {
                //Enter the Critical Section
                Monitor.Enter(this);
    
                NonCriticalSection();
    
                //Exit the Critical Section
                Monitor.Exit(this);
            }
    
            public static void Main(string[] args)
            {
                EnterExit e = new EnterExit();
    
                if (args.Length > 0)
                {
                    Thread nt1 =
                        new Thread(new ThreadStart(e.NonCriticalSection));
                    nt1.Start();
    
                    Thread nt2 =
                        new Thread(new ThreadStart(e.NonCriticalSection));
                    nt2.Start();
                }
                else
                {
                    Thread ct1 =
                        new Thread(new ThreadStart(e.CriticalSection));
                    ct1.Start();
    
                    Thread ct2 =
                        new Thread(new ThreadStart(e.CriticalSection));
                    ct2.Start();
                }
    
                Console.ReadLine();
            }
        }
    }

    当你不输入任何参数时你将得到CriticalSection()方法的输出,结果如下:

    2012-2-3 17-01-58

    对应的,当你输入一个参数时,将会得到NonCriticalSection() 方法的相关输出:

    2012-2-3 17-02-24

    上面的例子中,我们定义了一个EnterExit 类、一个全局变量和两个方法:NonCriticalSection() 和 CriticalSection(). 在NonCriticalSection()部分我们没有使用任何监视器来锁住它,而在 CriticalSection()方法中使用监视器来锁住关键部分。这两个方法都修改结果值。

    关键部分定义为Montor.Enter(this) 和 Monitor.Exit(this)之间的代码块。参数“this”表示锁应该按考虑设置在当前对象。把哪个对象作为Enter()方法的参数总是很难决定。当你需要锁住一个对象以便于其他线程不可以访问这个对象时,可以把相应对象的引用作为Enter()方法的参数。例如,在我们之前讨论的AccountWrapper例子中,我们把Account对象传递给Monitor, 而不是AccountWrapper对象的this指针。这是因为我们的目的是要锁住Account对象而不是AccountWrapper对象。我们不想让多个线程访问Account对象,但是不关注是否有多个对象访问AccountWrapper对象。

    在Main()方法中,我们按照输入的参数运行不同方法。如果没有输入参数,就是用CriticalSection()方法,反之则是用NonCriticalSection()方法。在两种情况下,我们都有两个线程在同一时间访问方法并改变结果值。尽管他们是按顺序定义的,For循环以及睡眠时间将保证线程完成资源。

    通过对比关键部分和非关键部分的输出可以让关键部分的概念更清晰。如果你观察NonCriticalSection()方法的输出,线程nt1 和 nt2 都在同一时间改变变量值,最终输出混乱结果。这是因为NonCriticalSection()方法没有锁,属于非线程安全的。多个线程可以访问这个方法并在同一时间获得全局变量值。另外一方面,如果你观察CriticalSection()方法的输出,结果很清晰的显示直到线程ct1退出关键部分,另外一个线程ct2才被允许进入关键部分。

    下一篇将介绍Wait() 和 Pulse() 机制…


    作者:DanielWise
    出处:http://www.cnblogs.com/danielWise/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    node.js 与java 的主要的区别是什么
    基于Node.js+MySQL开发的开源微信小程序B2C商城(页面高仿网易严选)
    Linux下SVN安装配置
    Linux查看CPU和内存使用情况
    Linux添加/删除用户和用户组
    java远程调试
    springboot和mybatis集成,自动生成model、mapper,增加mybatis分页功能
    客户端、服务端,跨域访问设置
    HTTP和HTTPS协议
    KMP算法代码
  • 原文地址:https://www.cnblogs.com/danielWise/p/2337342.html
Copyright © 2011-2022 走看看