pthread_mutex_lock当它是同一个线程

时间:2016-10-07 11:57:13

标签: c++ c multithreading mutex

我使用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();
}

假设我只有一个调用run函数的线程,所以: 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代码中得到相同的结果。

感谢。

4 个答案:

答案 0 :(得分:11)

正如Kami Kaze在评论中所指出的,如果这是您的完整示例,那么它就不是问题:只有一条通向get2的路径,而且此路径已经获得互斥;只是省略第二次获取它。

但是,一般来说,可以考虑不清楚的情况。在这种情况下,您可以制作互斥recursive/reentrant

  

在计算机科学中,可重入互斥(递归互斥,递归锁定)是特殊类型的互斥(互斥)设备,可以被同一进程/线程多次锁定,而不会导致死锁。

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

pthread_mutexattr_settype(&m_lock, PTHREAD_MUTEX_RECURSIVE);

答案 1 :(得分:0)

有了这个:

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

您已锁定整个get2()。因此,在get2()函数中再次使用相同的锁是没有意义的。 只需从get2()删除锁定和解锁代码。

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

  

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

在您的代码中, synchronized 区域不相互关联。因此,对于类似的比较,您需要在get2()函数中使用不同的互斥锁

答案 2 :(得分:0)

这称为锁定递归。

Jboss的最后一个参数是属性struct。您可以设置属性以允许使用pthread_mutex_init进行递归锁定。

但是,我必须在这里添加一些编辑内容。我非常强烈地相信锁定递归几乎总是一个错误。或者它将导致无法在程序生命周期的后期调试错误。

锁定操作可以被理解为表示&#34;当锁定函数返回时,由锁定保护的对象处于已知状态,并且在调用解锁函数之前该状态不会改变&#34;。这意味着如果pthread_mutexattr_settype(..., PTHREAD_MUTEX_RECURSIVE)已经开始使用锁定修改您保护的对象,然后get1递归该锁定,则此合约将被中断两次。首先是因为get2在对象未处于已知状态时成功获取锁定,其次是因为在get2认为它拥有锁定时修改了对象。

当然,我们经常会做这样的事情,但这是一种可怕的做法。重新设计您的程序以不递归锁定。执行此操作的标准方法是实现名为get1的函数,get2_locked获取锁定并调用get2,而get2_locked已知道它具有锁定并将调用{ {1}}。

答案 3 :(得分:0)

我认为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
}