pthread_mutex_lock同线程时如何不加锁

pthread_mutex_lock how to not lock when it is the same thread

我正在使用 pthread_mutex_t 进行锁定。

pthread_mutex_t m_lock;

void get1() {
    cout<<"Start get 1"<<endl;
    pthread_mutex_lock(&m_lock);
    get2();
    pthread_mutex_unlock(&m_lock);
    cout<<"End get 1"<<endl;
}

void get2() {
    cout<<"Start get 2"<<endl;
    pthread_mutex_lock(&m_lock); // The program actually stops here because it waits to m_lock to be unlock from get1 function.
    pthread_mutex_unlock(&m_lock);
    cout<<"End get 2"<<endl;
}

// The thread call to run function
void* run(void* p) {
    get1();
}

假设我只有一个调用 运行 函数的线程,所以: get1 锁定 m_lock 并调用 get2,但是当它尝试锁定 m_lock 时,它会等待锁被解锁(这并没有发生),我们遇到了死锁。

我的问题是,当在 get1 中锁定锁的同一个线程不需要等待 get2 中的锁(因为它是同一个线程)时,如何避免这种情况?

例如,在 Java 中,当您使用 synchornized 时,这种情况永远不会发生。

public Test implements Runnable {
    public void get1() {
        System.out.println("Start get 1");
        synchronized (this) {
            get2();
        }
        System.out.println("End get 1");
    }

    public void get2() {
        System.out.println("Start get 2");
        synchronized (this) {

        }
        System.out.println("End get 2");
    }

    @Override
    public void run() {
        get1();
    }
}

这里没有死锁。

请在我的 C 代码中得到相同的结果。

谢谢。

正如 Kami Kaze 在评论中指出的那样,如果这是您的完整示例,那么这不是问题:只有一条路径通往 get2,并且这条路径已经获得了互斥体;简单地省略第二次获取它。

但是,一般来说,可以想到不太清楚的场景。在这种情况下,您可以使互斥量 recursive/reentrant:

In computer science, the reentrant mutex (recursive mutex, recursive lock) is particular type of mutual exclusion (mutex) device that may be locked multiple times by the same process/thread, without causing a deadlock.

在您的设置中,这将通过 pthread_mutexattr_settype:

pthread_mutexattr_settype(&m_lock, PTHREAD_MUTEX_RECURSIVE);

有了这个:

pthread_mutex_lock(&m_lock);
get2();
pthread_mutex_unlock(&m_lock);

您已锁定整个 get2()。因此,在 get2() 函数中再次使用 same 锁是没有意义的。 只需从 get2().

中删除锁定和解锁代码

如果只有 get2() 中的代码部分需要锁定,则摆脱 get1() 函数的锁定和解锁。

For example, in Java this case never can happen when you use synchornized.

在您的代码中,同步 区域没有相互链接。因此,对于类似的比较,您需要在 get2() 函数中使用 不同的互斥体

这称为锁递归。

pthread_mutex_init 的最后一个参数是属性结构。您可以设置属性以允许使用 pthread_mutexattr_settype(..., PTHREAD_MUTEX_RECURSIVE).

进行递归锁定

但是,我必须在这里添加一些编辑内容。我坚信锁递归几乎总是一个错误。否则将导致无法在程序生命周期后期调试错误。

锁定操作可以推断为"when the lock function returns the object protected by the lock is in a known state and this state will not change until the unlock function is called"。这意味着如果 get1 已经开始修改你用锁保护的对象,然后 get2 递归那个锁,这个契约就被打破了两次。首先是因为 get2 在对象不处于已知状态时成功获得了锁,其次是因为对象被修改而 get1 认为它拥有锁。

当然,我们经常这样做,但这是一种糟糕的做法。重新设计您的程序以不递归锁。做到这一点的标准方法是实现一个名为 get2_locked 的函数,get2 获得锁并调用 get2_lockedget1 已经知道它有锁并会调用 get2_locked.

我假设 get1 真的不仅仅是获取锁并调用 get2?否则 get1 有什么意义?

如果是这种情况,您可以通过 get3 函数来解决它,该函数执行 get2 的主要部分(您未在此处显示的部分)并且不锁定。然后改为从 get1 调用该新函数(当然也从 get 调用):

void get1()
{
    // Do something here

    cout<<"Start get 1"<<endl;
    pthread_mutex_lock(&m_lock);
    get3();  // <-- Note call get3 instead here
    pthread_mutex_unlock(&m_lock);
    cout<<"End get 1"<<endl;

    // Do something more here
}

void get2()
{
    cout<<"Start get 2"<<endl;
    pthread_mutex_lock(&m_lock); // The program actually stops here because it waits to m_lock to be unlock from get1 function.
    get3();  // <-- Note call to get3 here
    pthread_mutex_unlock(&m_lock);
    cout<<"End get 2"<<endl;
}

void get3()
{
    // Do the actual work of get2 here...
    // Note: No locking here
}