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_locked
而 get1
已经知道它有锁并会调用 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
}
我正在使用 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_locked
而 get1
已经知道它有锁并会调用 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
}