• 炸弹问题——一种会引发死锁的情景模式


    有这样一种炸弹,它有一个控制中心,时不时发送消息给它;当它被引爆的时候,它将告诉控制中心, 并把自己从控制中心的联络列表中移除。在通常的代码中,由于接受消息或者引爆的时候,都会引起炸弹的状态变化,所以,我们会使用一个锁来保证这两个操作是有顺序的;另外,控制中心的控制列表也是会有锁来控制访问。下面是其中一个实现:

    https://github.com/AmongOthers/BombsProblem/commits/deadlock_version

    运行过程中将会出现死锁的情况。原因是:

    Bomb:
    public void Fire() { log("Bomb.Fire: lock core begin"); lock (mCoreLock) { log("Bomb.Fire: lock core end"); mCenter.RemoveBomb(this); fire(); } }
    BombCenter:    
       public void RemoveBomb(Bomb bomb) { log("BombCenter removeBomb: lock bombs begin"); lock (mBombsLock) { log("BombCenter removeBomb: lock bombs end"); mBombs.Remove(bomb); } }

    上面的过程需要的锁的顺序是:Bomb.mCoreLock->BombCenter.mBombsLock

    而控制中心呼叫炸弹的过程:

    BombCenter:     
       public void callBombs() { log("BombCenter fireBombs: lock bombs begin"); lock (mBombsLock) { log("BombCenter fireBombs: lock bombs end"); foreach (var bomb in mBombs) { bomb.OnCommandReceived(); } } }
                
      Bomb:
           public void OnCommandReceived() { log("Bomb.onCommandReceived: lock core begin"); lock (mCoreLock) { log("Bomb.onCommandReceived: lock core end"); dumb(); } }

    需要的锁的顺序是: BombCetner.mBombsLock->Bomb.mCoreLock

    当这两个过程并发进行的话,就有可能会造成死锁的情况。本质上来说,这是"哲学家吃饭问题"的变种,但是它比较隐蔽,一般来说,会有一个消息的订阅者的基本模式,而订阅者会自我销毁,并主动要求消息中心从列表中移除自己。这样的套路用的还是蛮多的,但是有可能会产生死锁的问题。

    一种解决方案是,炸弹不直接要求控制中心移除自己,而是设置自己一个“可以被废弃”的标志,而由控制中心在适当的时候移除。

    https://github.com/AmongOthers/BombsProblem/commits/set_remove_flag_version

    BombCenter:
    public void callBombs() { log("BombCenter fireBombs: lock bombs begin"); lock (mBombsLock) { mBombs.RemoveAll((bomb) => { return bomb.IsFired; }); log("BombCenter fireBombs: lock bombs end"); foreach (var bomb in mBombs) { bomb.OnCommandReceived(); } } }

    但是如果账单中心没有所谓的合适的地方放置移除标记了的炸弹(没有自己的线程),那么另外一种解决方案就是,使用"异步移除"。

    https://github.com/AmongOthers/BombsProblem/tree/async_remove

    Bomb:
                public void Fire()
                {
                    log("Bomb.Fire: lock core begin");
                    lock (mCoreLock)
                    {
                        log("Bomb.Fire: lock core end");
                        ThreadPool.QueueUserWorkItem(delegate
                        {
                            mCenter.RemoveBomb(this);
                            fire();
                        });
                    }
                }

    ———————————————————————————————————————————————————————————————————————————————

     炸弹问题的本质是,炸弹的事件引起控制中心的某个数据集合发生变化,而控制中心的线程会遍历集合,对炸弹进行操作。如果控制中心弱化成只是单纯的数据集合,那么就不属于炸弹问题。

  • 相关阅读:
    空间统计笔记之三(度量地理分布工具集,Measuring Geographic Distributions)
    空间统计笔记之四(空间关系建模工具集,Modeling Spatial Relationships)
    黑苹果WiFi&蓝牙驱动
    Hackintosh问题集结
    macOS读写NTFS磁盘分区
    Windows10 无法打开此计算机上的组策略对象。你可能没有相应的权限
    电脑C盘容量扩容
    Windows配置定时休眠和唤醒
    macOS Windows添加静态路由
    macOS使用技巧
  • 原文地址:https://www.cnblogs.com/zhengwenwei/p/2755502.html
Copyright © 2020-2023  润新知