zoukankan      html  css  js  c++  java
  • 一个多线程示例程序的BUG修复

    一个多线程示例程序的BUG修复



        在《.NET 4.0面向对象编程漫谈 》的《应用篇》一书中,我介绍了一个使用“信号量(Semaphore) ”同步对象模拟多人使用图书馆公共计算机的UseLibraryComputer示例(参看17.3.3节《管理多个共享资源--Semaphore》):



     

    图 1 UseLibraryComputer示例

        此示例程序定义了一个Computer类代表计算机,使用一个Computer对象数组(取名LibraryComputers)保存图书馆所拥有的多台公共计算机(即Computer对象)。
        示例程序动态创建多个线程模拟多用户(每个线程代表一个用户)同时访问3个Computer对象,为了同步这些线程,创建了一个Semaphore对象(取名sp),以下为示例中的线程函数:

            static void UseComputer(Object UserName)
            {
                sp.WaitOne();//等待计算机可用
                //查找可用的计算机
                Computer cp=null;
                for (int i = 0; i < ComputerNum; i++)
                    if (LibraryComputers[i].IsOccupied == false)
                    {
                        cp = LibraryComputers[i];
                        break;
                    }
                //使用计算机工作
                cp.Use(UserName.ToString());
                //不再使用计算机,让出来给其他人使用
                sp.Release();
            }

        Computer类中定义了一个IsOccupied字段用于标识此计算机是否被占用,其Use方法模拟表示用户正在使用计算机的过程:

        //是否被占用
        public  bool IsOccupied = false;
        //读者在使用计算机
        public  void Use(String userName)
        {
            Console.WriteLine("{0}开始使用计算机{1}", userName,ComputerName);
            IsOccupied = true;  //设置占用标记
            Thread.Sleep(new Random().Next(1, 2000)); //随机休眠,以模拟人使用计算机
            Console.WriteLine("{0}结束使用计算机{1}", userName,ComputerName);
            IsOccupied = false; //设置空闲标记
        }

        前述示例代码中隐藏着一个BUG,请看图2:


     图 2

        BUG隐藏于何处?
        请读者先仔细阅读原示例程序 ,尝试着自己定位并修正此BUG。
        以下是BUG分析:
        这里面的关键是Semaphore对象可能会一次允许多个线程投入运行,而这些投入运行的线程都需要访问LibraryComputers数组(请参看前面的UseComputer静态方法)。
        假设有3台计算机空闲,当示例程序第1次运行时,会有3个线程同时访问LibraryComputers数组,完全有可能两个线程都发现第1台计算机是“空闲”的,于是它们将调用此Computer对象的Use()方法。
        这就是BUG所在。
        定位BUG之后,要修正它就很容易了--互斥访问LibraryComputers数组即可
        修改后的线程函数如下:

            static void UseComputer(Object UserName)
            {
                sp.WaitOne();//等待计算机可用
                Computer cp=null; //查找可用的计算机
                lock (LibraryComputers)
                {
                    for (int i = 0; i < ComputerNum; i++)
                        if (LibraryComputers[i].IsOccupied == false)
                        {
                            cp = LibraryComputers[i];
                            cp.IsOccupied = true;  //设置已被使用标记
                            break;
                        }
                }
                cp.Use(UserName.ToString());//使用计算机工作
                sp.Release();//不再使用计算机,让出来给其他人使用
            }

        注意:上述代码中不仅添加了锁,还将修改Computer对象占用标记的代码(原先在Computer.Use方法中)也移到这里来了。
        以下修改后的Computer.Use()方法:

        public  void Use(String userName)
        {
            Console.WriteLine("{0}开始使用计算机{1}", userName,ComputerName);
            Thread.Sleep(new Random().Next(1, 2000)); //随机休眠,以模拟人使用计算机
            Console.WriteLine("{0}结束使用计算机{1}", userName,ComputerName);
            IsOccupied = false; 
        }

        最后留给读者一个思考题:
        Computer.Use()方法中修改IsOccupied字段的那句代码需要锁定本对象吗?


    ============================================================================
       


    附注:
        此示例程序中的BUG由CSDN网友“笑傲江湖 ”发现,是本人在设计示例时的疏漏,在此特别感谢“笑傲江湖" 网友的热心!也欢迎各位读者或业界朋友多指正本人拙著中的错误,互相探讨技术,共同进步。

  • 相关阅读:
    html5标签---不常用新标签的整理
    拖拽demo--兼容--全局捕获
    Linux now!--网络配置
    windows下 memcached 和 redis 服务器安装
    MySQL5.6安装步骤(windows7/8_64位)
    zend 环境
    mysql自增id超大问题查询
    烦人的运营后台导出大批量数据
    kafka环境搭建和使用(python API)
    分布式系统中zookeeper实现配置管理+集群管理
  • 原文地址:https://www.cnblogs.com/bitfan/p/1935482.html
Copyright © 2011-2022 走看看