代码之家  ›  专栏  ›  技术社区  ›  foraidt

如何使用CSingleLock提供对此缓冲区的访问?

  •  0
  • foraidt  · 技术社区  · 15 年前

    我有这两种方法用于以线程独占方式访问 CMyBuffer

    标题:

    class CSomeClass
    {
    //...
    public:
        CMyBuffer & LockBuffer();
        void ReleaseBuffer();
    
    private:
        CMyBuffer m_buffer;
        CCriticalSection m_bufferLock;
    //...
    }
    

    实施:

    CMyBuffer & CSomeClass::LockBuffer()
    {
        m_bufferLock.Lock();
        return m_buffer;
    }
    
    void CSomeClass::ReleaseBuffer()
    {
        m_bufferLock.Unlock();
    }
    

    用法:

    void someFunction(CSomeClass & sc)
    {
        CMyBuffer & buffer = sc.LockBuffer();
        // access buffer
        sc.ReleaseBuffer();
    }
    
    • 我喜欢的是 调用,并且只能访问缓冲区 锁上之后。
    • 我不知道的是什么 就像用户必须释放

    使现代化 :Nick Meyer和Martin York指出了这些额外的缺点:

    • 用户可以释放锁,然后使用缓冲区。
    • 如果在释放锁之前发生异常,缓冲区将保持锁定状态。

    我想用一只手做这件事 CSingleLock 对象(或类似对象),它在对象超出范围时解锁缓冲区。

    这怎么可能呢?

    3 回复  |  直到 15 年前
        1
  •  2
  •   Naveen    15 年前

    一种方法是使用 RAII :

    class CMyBuffer
    {
    public:
        void Lock()
        {
            m_bufferLock.Lock();
        }
    
        void Unlock()
        {
            m_bufferLock.Unlock();
        }
    
    private:
        CCriticalSection m_bufferLock;
    
    };
    
    class LockedBuffer
    {
    public:
        LockedBuffer(CMyBuffer& myBuffer) : m_myBuffer(myBuffer)
        {
            m_myBuffer.Lock();
        }
    
        ~LockedBuffer()
        {
    
            m_myBuffer.Unlock();
        }
    
        CMyBuffer& getBuffer() 
        {
            return m_myBuffer;
        }
    
    private:
        CMyBuffer& m_myBuffer;
    
    };
    
    class CSomeClass
    {
        //...
    public:
        LockedBuffer getBuffer();
    
    private:
        CMyBuffer m_buffer;
    
    };
    
    
    LockedBuffer CSomeClass::getBuffer()
    {
        return LockedBuffer(m_buffer);
    }
    
    void someFunction(CSomeClass & sc)
    {
        LockedBuffer lb = sc.getBuffer();
        CMyBuffer& buffer = lb.getBuffer();
        //Use the buffer, when lb object goes out of scope buffer lock is released
    }
    
        2
  •  1
  •   Nick Meyer    15 年前

    IMHO,如果你的目标是防止用户仅在缓冲区被锁定时访问缓冲区,那么你正在进行一场棘手的战斗。考虑用户是否:

    void someFunction(CSomeClass & sc)
    {
       CMyBuffer & buffer = sc.LockBuffer();
       sc.ReleaseBuffer();
       buffer.someMutatingMethod(); // Whoops, accessed while unlocked!
    }
    

    为了允许用户访问缓冲区,您必须返回一个对缓冲区的引用,他们总是会错误地将其保留到锁释放后。

    也就是说,您可能可以这样做:

    class CMyBuffer
    {
       private:
          void mutateMe();
          CCriticalSection m_CritSec;
    
       friend class CMySynchronizedBuffer;
    };
    
    class CMySynchronizedBuffer
    {
       private:
          CMyBuffer & m_Buffer;
          CSingleLock m_Lock
    
       public:
          CMySynchronizedBuffer (CMyBuffer & buffer)
             : m_Buffer (buffer)
             , m_Lock (&m_Buffer.m_CritSec, TRUE)
          {
          }
    
          void mutateMe()
          {
             m_Buffer.mutateMe();
          }
    };
    

    使用类似于:

    {
       CMyBuffer buffer;  // Or declared elsewhere
       // buffer.mutateMe();  (Can't do this)
       CMySyncrhonizedBuffer synchBuffer (buffer); // Wrap it & lock it
       synchBuffer.mutateMe();  // Now protected by critical section
    } // synchBuffer and its CSingleLock member are destroyed and the CS released
    

    这会强制用户将CMyBuffer对象包装在CMySynchronizedBuffer对象中,以获取其任何变化方法。因为它实际上不通过返回引用来提供对底层CMyBuffer对象的访问,所以它不应该给用户任何可以挂起的东西,也不应该在释放锁后进行修改。

        3
  •  1
  •   Loki Astari    15 年前

    使用表示缓冲区的对象。

    添加一个cast运算符,以便在任何函数调用中使用它来代替缓冲区:

    #include <iostream>
    
    // Added to just get it to compile
    struct CMyBuffer
    {    void doStuff() {std::cout << "Stuff\n";}};
    struct CCriticalSection
    {
            void Lock()     {}
            void Unlock()   {}
    };          
    
    class CSomeClass
    {
        private:
            CMyBuffer m_buffer;
            CCriticalSection m_bufferLock;
    
            // Note the friendship.
            friend class CSomeClassBufRef;
    };
    
    // The interesting class.
    class CSomeClassBufRef
    {
        public:
            CSomeClassBufRef(CSomeClass& parent)
                :m_owned(parent)
            {
               // Lock on construction
                m_owned.m_bufferLock.Lock();
            }
            ~CSomeClassBufRef()
            {
                // Unlock on destruction
                m_owned.m_bufferLock.Unlock();
            }
            operator CMyBuffer&()
            {
                // When this object needs to be used as a CMyBuffer cast it.
                return m_owned.m_buffer;
            }
        private:
            CSomeClass&     m_owned;
    }; 
    
    void doStuff(CMyBuffer& buf)
    {           
        buf.doStuff();
    }
    int main()
    {
        CSomeClass          s;
    
        // Get a reference to the buffer and auto lock.
        CSomeClassBufRef    b(s);
    
        // This call auto casts into CMyBuffer
        doStuff(b);
    
        // But you can explicitly cast into CMyBuffer if you need.
        static_cast<CMyBuffer&>(b).doStuff();
    }