• 【转】Solmyr 的小品文系列之六:成对出现


    http://www.cppblog.com/xmli/archive/2009/08/19/93797.html

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

    “呼 ~~~~ 啪!” 

    一个文件夹划出一道优美的弧线,越过四张桌子,两堵隔墙,一条走道,不偏不倚的穿过了正在交谈的路人甲和路人乙,精准的命中了目标。放眼公司上下,拥有这般投掷手法的,只有 Solmyr ,而他的目标,自然是 zero 了。 

    “哎哟!”,zero 摸了摸被击中的后脑勺,一半不甘一半认命的叹了一口气:不用问,他一定又有什么把柄被 Solmyr 抓住了。 

    “这次我又犯了什么错误了?”,zero 匆匆中断了与方圆五十米内唯一的女程序员 pisces 之间愉快的闲聊,来到 Solmyr 身边看看究竟哪里出了不妥。 

    “你刚刚提交的代码会导致线程死锁”,Solmyr 指着 zero 提交的一个函数: 

    void some_func() 

    pthread_mutex_lock(&mtx); 
    …… 
    …… 
    pthread_mutex_unlock(&mtx); 

    “会吗?我明明在函数末尾释放了互斥变量的呀?” 

    Solmyr 看了看 zero ,那表情分明在说:朽木不可雕也。他顺手标出了函数中间的两行代码: 

    void some_func() 

    pthread_mutex_lock(&mtx); 
    …… 
    if( status == E_FAIL ) 
     return; 
    …… 
    pthread_mutex_unlock(&mtx); 

    “Oops!”,zero 拍了一下脑门,“我知道了我知道了,我这就改。” 

    “你知道了?说说看你犯了什么错误?” 

    “我忘了在中间的函数返回点解锁。” 

    “那你准备怎么解决这个问题”,很明显,Solmyr 不打算就此轻轻放过 zero。 

    “嗯 …… 很简单啊,在这里加上一行代码,象这样:” 

    if( status == E_FAIL ) 

    pthread_mutex_unlock(mtx); 
    return; 

    Solmyr 摇摇头:“你这是头痛医头,脚痛医脚。如果你这个函数里不只一个锁,不只一个返回点,你打算怎么做?在每个返回点解开每个锁么?” 

    “嗯 …… 你是指我应该遵循一个函数只有一个返回点的原则?”,zero 挠挠头,有些不太确定。 

    “我不是指这个。有些情况下,硬要让函数只有一个返回点会导致巨大的 if/else 结构,降低代码的可读性。而且,即使你的函数只有一个返回点,你还是有可能遇到这个问题。考虑这样的函数:”,Solmyr 飞快的键入: 

    void some_func() 

    pthread_mutex_lock(&mtx); 
    …… 
    // 中间没有其他返回点 
    …… 
    foo(); // 由其他程序员实现的函数 
    …… 
    pthread_mutex_unlock(&mtx); 

    “看起来一点问题也没有,可是如果 foo 这个函数丢出异常的话,会出现什么情况?” 

    “嗯 …… 如果我们函数里没有捕获这个异常的话 …… 它会导致 some_func 函数在调用 foo 的这一点中断 …… 哎呀 ……”,zero 发现了问题所在。“那么只能在每个可能抛出异常的函数调用点用 try 捕获所有异常,然后 ……”,zero 越说越小声,“ …… 然后在 catch 里面解锁,再重新抛出 ……” zero 停了下来,烦恼的挠着头,发现他连自己都说服不了:这样的解法实在是太繁琐、太容易引入错误了。 

    “嗯?” 

    “好吧,我承认我不知道该怎么办了,Solmyr ,这种情况应该怎么处理呢?” 

    “回忆一下,前两天我们在饭桌上讨论过什么?”(参见“Solmyr 的小品文系列”的前一期,“垃圾收集”) 

    “你是说垃圾收集吗?哎 …… 可是 …… 那是处理内存泄漏的呀?和这个问题有什么关系?” 

    “我不是指具体的解法,”,Solmyr 摇摇头,“关键是上次讨论中引入的具有普遍性的原则,也就是 ……” Solmyr 停了下来,转头看着 zero 。 

    “…… ……” 

    “唉 ……”,Solmyr 用别人模仿不来的无奈表情 —— 按照他自己的说法,这是多年培训工作的积累 —— 叹了口气:“我说 zero,你还很年轻,不会这么早就记忆力衰退了吧?” 

    …… 真是可恶的家伙,zero 心中恨恨的想。 

    Solmyr 的声音再度在 zero 接近崩溃边缘的时候响了起来:“如果你希望保证某些事情成对出现,请使用 ……” 

    “构造函数与析构函数!”,zero 生怕错过了显示自己并非“记忆力衰退”的机会。 

    “不用喊那么大声。”,Solmyr 皱了皱眉,“你把前排观众都吓坏了。” 

    “?!!!”,zero 迅速转身,发现附近不知什么时候围满了公司的同事,每个人都“正常”在做自己的事情,只是动作稍显忙乱而已 …… 

    解决了四周的“观众”之后,zero 回到了显示器前,信心满满:“我知道了 Solmyr ,这里我们可以用和上次处理 分配/释放 内存非常类似的手段来处理 加锁/解锁,只要写一个非常简单的类就行了,象这样:”,zero 一边说,一边键入: 

    class auto_lock 

    public: 
    auto_lock(pthread_mutex_t mtx) : m_mtx(mtx) 

     pthread_mutex_lock(&m_mtx); // 构造时加锁 

    ~auto_lock() 

     pthread_mutex_unlock(&m_mtx); // 析构时解锁 

    private: 
    pthread_mutex_t& m_mtx; 

    void some_func() 

    auto_lock(mtx); 
    …… 
    // return 、foo ,随便什么东西都行 
    …… 
    // 结束的时候同样不用解锁 

    “这样一来,我之前遇到的问题就全解决了,我可以自由的实现我的函数,不论什么时候返回或者遇到异常,我都可以肯定 mtx 将会被解锁,不用担心线程死锁的问题。” 

    “嗯, 不错。” Solmyr 赞许的点了点头,开始总结:“实际上这是一个非常常用的手段,除了我们讨论过的两种情况而外,还可以应用在很多场合。比如网络访问中的建立连接和断开连 接,数据库访问中的登录与退出登录,还可以方便的用它来实现测量一个函数平均运行耗时的测试工具,等等等等。不过万变不离其宗,在这一切应用的背后是一个 统一的原则 ……” 

    Solmyr 顿了一顿,zero 心领神会的接了上去: 

    “如果你希望保证某些事情成对出现,请使用构造函数与析构函数。”

  • 相关阅读:
    剑指Offer-11.二进制中1的个数(C++/Java)
    剑指Offer-10.矩形覆盖(C++/Java)
    剑指Offer-9.变态跳台阶(C++/Java)
    UVA 1608 Non-boring sequence 不无聊的序列(分治,中途相遇)
    UVA1607 Gates 与非门电路 (二分)
    UVA 1451 Average平均值 (数形结合,斜率优化)
    UVA 1471 Defense Lines 防线 (LIS变形)
    UVA 1606 Amphiphilic Carbon Molecules 两亲性分子 (极角排序或叉积,扫描法)
    UVA 11134 FabledRooks 传说中的车 (问题分解)
    UVA 1152 4 Values Whose Sum is Zero 和为0的4个值 (中途相遇)
  • 原文地址:https://www.cnblogs.com/iammatthew/p/1806968.html
Copyright © 2020-2023  润新知