zoukankan      html  css  js  c++  java
  • volatile 和 LockingPtr(原创)

    volatile 和 LockingPtr(原创):

    作者: wink

    声明: 此文章允许被转载, 但是请尊重作者, 请注明出处.谢谢

      众所周知, 我不爱写文章, 但是今天确实很有冲动写这篇文章, 望各位看官一定注意. 下面的内容对大家多线程安全编程必有好处.

      写过多线程程序的人都知道, 写出多线程安全的代码, 而且要优雅的代码, 是很困难的事. 特别是当使用STL的时候, 经常会忘记加锁, 这个问题不可谓不烦人.

      下面说一种机制, 可以让编译器告知你, 你的代码有线程安全问题..听起来多么惬意的一件事情啊...是的, 本篇文章的主角就是大名鼎鼎的volatile...

      先说说volatile的知识.

    class Gadget
    {
    public:
        void Foo() volatile;
        void Bar();
        ...
    private:
        String name_;
        int state_;
    };
    Gadget regularGadget;
    volatile Gadget volatileGadget;
    当你调用
    volatileGadget.Foo();   // 没有问题
     
    regularGadget.Foo();   // 没有问题
    volatileGadget.Bar(); // 编译出错, volatile的对象不可以调用非volatile的成员函数 

    该怎么办呢, 我们都知道const和volatile是对立的, 这里可以用强制转型, 把对象的volatile去掉

    Gadget& ref = const_cast<Gadget&>(volatileGadget);
    ref.Bar();  // 这样是可以的

    或许你觉得, 对一个对象用volatile, 很怪异, 这样到底对我们的代码到底有什么好处呢.

    大家继续耐心的看下去. 

    比如我们在类里面使用了stl, 其实任何一个程序员都不敢保证自己一定在使用它的时候加了锁, 这个时候, 如果我们对这个stl对象加上volatile关键字的话. 马上就会见分晓

    我们先实现一个平台独立的Mutex类(在windows下就是关键区CRITICAL_SECTION) 

    class Mutex
    {
    public:
        void Acquire();
        void Release();
        ...    
    };
    class SyncBuf {
    public:
        void Thread1();
        void Thread2();
    private:
        typedef vector<char> BufT;
        volatile BufT buffer_;  // 注意这里!!
        Mutex mtx_; // 用于同步BufT
    };

    当你在函数中 使用buffer_ 而不加锁的时候. 编译器会很优雅的告诉你. 你出错了

    void SyncBuf::Thread2() {
        BufT::iterator i = buffer_.begin(); // 出错, 因为volatile对象buffer_不能访问非volatile成员函数, 而begin不是一个volatile成员函数
        for (; i != lpBuf->end(); ++i) {
            ... use *i ...
        }
    }

    感觉到很优雅了吗? 那么, 这个时候, 我们该如何方便的使用stl的buffer_呢? 

    下面, 另一个主角要出场了 . 我们实现LockingPtr

    template <typename T>
    class LockingPtr {
    public:
       LockingPtr(volatile T& obj, Mutex& mtx)
           : pObj_(const_cast<T*>(&obj)),  // 注意这里的const_cast<T*>, 关键!!
            pMtx_(&mtx)
       {    mtx.Acquire();    }
       ~LockingPtr()
       {    pMtx_->Release();    }
       // Ptr
       T& operator*()
       {    return *pObj_;    }
       T* operator->()
       {   return pObj_;   }
    private:
       T* pObj_;
       Mutex* pMtx_;
       LockingPtr(const LockingPtr&);
       LockingPtr& operator=(const LockingPtr&);
    };

    现在, 我们可以在代码中既方便, 又优雅的使用STL了.

    void SyncBuf::Thread1() {
        LockingPtr<BufT> lpBuf(buffer_, mtx_);
        BufT::iterator i = lpBuf->begin();
        for (; i != lpBuf->end(); ++i) {
            ... use *i ...
        }
    }

    看到了没有, 上面是不会出错的, 而且不失方便性.

    总结:

      你应该在任何多线程共享的对象或者变量前面加上volatile关键字, 这是多线程编程的好帮手.

       其实volatile不止这个作用的, 有时候编译器为了优化, 把变量直接用寄存器保存, 那么这个时候, 其他线程如果改变了变量的话, 当前线程是根本不会发觉的. 加上了volatile的话, 就不存在这个问题了

        

     

     

     

     

     

      

  • 相关阅读:
    JSON的使用总结
    pc端分页插件的使用
    简单修改选择文件样式
    H5中的本地存储
    H5中的 meta 标签及 移动页面单位
    1001. A+B Format (20)
    查看mysql的注册表路径
    win10 64位安装mysql
    [POLITICS] S Korea lawmakers vote to impeach leader
    5-17 Hashing (25分)
  • 原文地址:https://www.cnblogs.com/winkyao/p/2614488.html
Copyright © 2011-2022 走看看