确保某些私有函数只能从锁定状态调用

时间:2009-11-20 16:10:44

标签: c++ locking deadlock

说我有一个班级A

class A {
public:
   A();

   void fetch_data() { return 1; }
   void write_x_data() {
     // lock this instance of A
     private_function1_which_assumes_locked();
     private_function2_which_assumes_locked();
     // unlock this instance of A
   }
   void write_y_data() {
     // lock this instance of A
     private_function1_which_assumes_locked();
     // unlock this instance of A
   }
private:
   void private_function1_which_assumes_locked();
   void private_function2_which_assumes_locked();
};

我想保证永远不会调用private_function*_which_assumed_locked(),除非A被锁定。

实现这一目标的最佳方法是什么?我有5个左右需要锁定的公共函数。这些函数从不相互调用,所以我并不担心这些函数会陷入僵局。结合起来,这5个公共函数调用大约15个不同的私有函数,这些函数需要假设对象处于锁定状态。显然,我无法锁定私有函数,因为我会遇到死锁。

假设存在互斥锁和Scopeguards,请随意在合理的高级抽象中提供答案。

在Python中,我可以使用decorators 执行之类的

class locked_method(object):
    def __init__(self, f):
        self.f = f

    def __call__(self):
        # Do whatever is needed to lock
        self.f()
        # Do whatever is needed to unlock

class checklocked(object):
    def __init__(self, f):
        self.f = f

    def __call__(self):
        # check locked, if not, don't call self.f(),
        # and yell at me loudly for screwing up.
        self.f()

@locked_method
def func1():
    __function_which_assumes_locked()

@checklocked
def __function_which_assumes_locked():
    pass

注意:我对Python的方式做得不多,所以如果我的Python错误/愚蠢,请随意评论,但这个问题的重点更多是在C ++中完成此类事情的最佳方法,所以希望Python提供的内容足以让你知道我想做什么。

6 个答案:

答案 0 :(得分:4)

您可以使用一个更衣室类,并且要求存在一个以便调用私有函数:

class A
{
public:
    void write()
    {
        Lock l(this);
        write(l);
    }

private:
    void lock();
    void unlock();
    void write(const Lock &);

    class Lock
    {
    public:
        explicit Lock(A *a) : parent(a) {parent->lock();}
        ~Lock() {parent->unlock();}
    private:
        A *parent;
    };
};

答案 1 :(得分:2)

这让我想起了Dr. Dobb's article by Andrei Alexandrescu

这个想法是使用这样一个事实:就像const成员函数不能调用非const成员函数一样,volatile成员不能调用非易失性成员。然后他使用volatile来“标记”线程安全函数,而非易失性函数则“不安全”。所以这无法编译:

class Foo
{
public:
    // Not the "volatile"
    int External() volatile
    {
        return this->Internal();
    }

private:

    // Not volatile
    int Internal();
};

因为this无法从Foo volatile*转换为Foo*。演员是必要的。这是通过一个辅助类来实现的,它也扮演着RAII锁定持有者的角色。所以Foo变成了:

class Foo
{
public:
    int External() volatile
    {
        Lock self(this);
        return self->Internal();
    }

private:

    class Lock{
    public:
        explicit Lock(volatile Foo* Self)
            : m_Mutex.Aquire(),
              m_Self(const_cast<Foo*>(Self))
        {}

        ~Lock()
        {
            m_Mutex.Release();
        }

        Foo* operator->(){
            return m_Self;
        }

    private:
        SomeMutexType m_Mutex;
        Foo* m_Self;
    };


    int Internal();
};

答案 2 :(得分:0)

您可以使用'#Define'创建类似的效果:

#define CheckIfLocked(func, criticalSection) \
    // here you can enter the critical section
    func \
    // exit critical section

答案 3 :(得分:0)

您可以使用C ++之一locking mechanisms

使用其中一个“Try”-ing函数作为每个私有函数的第一步 - 这些函数只检查锁是否被锁定。如果未锁定,则抛出异常。

P.S。确保私有函数仅在 compile -time中从锁定状态调用...可能会很有趣...这可能在Haskell中可能:)

答案 4 :(得分:0)

lock()函数锁定互斥锁并将线程id放入成员变量中。 unlock()将成员变量设置为0并解锁互斥锁

在你的私人函数中检查线程id是你自己的线程的,并且如果不是那么大声抱怨。

答案 5 :(得分:0)

在每个公共函数的开头锁定对象A一次,并在处理完成后释放锁定。公共职能是处理开始的入口点。您不必担心锁定每个私有函数。

查看RAII以锁定/解锁对象(或使用类似boost::mutex::scoped_lock的内容)。