简体   繁体   English

确保线程没有锁定互斥锁两次?

[英]Ensure that a thread doesn't lock a mutex twice?

Say I have a thread running a member method like runController in the example below: 假设我在下面的示例中有一个运行runController之类的成员方法的线程:

class SomeClass {
public:
    SomeClass() { 
         // Start controller thread
         mControllerThread = std::thread(&SomeClass::runController, this) 
    }

    ~SomeClass() {
         // Stop controller thread
         mIsControllerThreadInterrupted = true;
         // wait for thread to die.
         std::unique_lock<std:::mutex> lk(mControllerThreadAlive); 
    }

    // Both controller and external client threads might call this
    void modifyObject() {
         std::unique_lock<std::mutex> lock(mObjectMutex);
         mObject.doSomeModification();
    }
    //...
private:
    std::mutex mObjectMutex;
    Object mObject;

    std::thread mControllerThread;
    std::atomic<bool> mIsControllerInterrupted;
    std::mutex mControllerThreadAlive;

    void runController() {        
        std::unique_lock<std::mutex> aliveLock(mControllerThreadAlive);
        while(!mIsControllerInterruped) {
            // Say I need to synchronize on mObject for all of these calls
            std::unique_lock<std::mutex> lock(mObjectMutex);
            someMethodA();
            modifyObject(); // but calling modifyObject will then lock mutex twice
            someMethodC();
        }
    }
    //...
};

And some (or all) of the subroutines in runController need to modify data that is shared between threads and guarded by a mutex. 并且runController一些(或所有)子例程需要修改线程之间共享并由互斥锁保护的数据。 Some (or all) of them, might also be called by other threads that need to modify this shared data. 其中一些(或全部)也可能被需要修改此共享数据的其他线程调用。

With all the glory of C++11 at my disposal, how can I ensure that no thread ever locks a mutex twice? 有了C ++ 11的所有荣耀,我怎样才能确保没有线程曾经两次锁定互斥锁?

Right now, I'm passing unique_lock references into the methods as parameters as below. 现在,我将unique_lock引用作为参数传递给方法,如下所示。 But this seems clunky, difficult to maintain, potentially disastrous, etc... 但这似乎很笨重,难以维护,可能是灾难性的......等等......

void modifyObject(std::unique_lock<std::mutex>& objectLock) {

    // We don't even know if this lock manages the right mutex... 
    // so let's waste some time checking that.
    if(objectLock.mutex() != &mObjectMutex)
         throw std::logic_error();

    // Lock mutex if not locked by this thread
    bool wasObjectLockOwned = objectLock.owns_lock();
    if(!wasObjectLockOwned)
        objectLock.lock();

    mObject.doSomeModification();

    // restore previous lock state
    if(!wasObjectLockOwned)
        objectLock.unlock();

}

Thanks! 谢谢!

There are several ways to avoid this kind of programming error. 有几种方法可以避免这种编程错误。 I recommend doing it on a class design level: 我建议在类设计级别上进行:

  • separate between public and private member functions, 公共私人成员职能分开,
  • only public member functions lock the mutex , 只有公共成员函数锁定互斥锁
  • and public member functions are never called by other member functions. 其他成员函数从不调用公共成员函数。

If a function is needed both internally and externally, create two variants of the function, and delegate from one to the other: 如果内部和外部都需要函数,则创建函数的两个变体,并从一个变为另一个:

public:
    // intended to be used from the outside
    int foobar(int x, int y)
    {
         std::unique_lock<std::mutex> lock(mControllerThreadAlive);
         return _foobar(x, y);
    }
private:
    // intended to be used from other (public or private) member functions
    int _foobar(int x, int y)
    {
        // ... code that requires locking
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM