简短的问题:POSIX线程API是否为我提供一种确定调用线程是否已持有特定锁的方法?
长问题:
假设我想用锁保护数据结构。获取和释放锁需要在不同的功能中发生。涉及的函数之间的调用相当复杂(我正在将多线程添加到已有16年历史的代码库中)。例如:
do_dangerous_stuff()
执行当前线程需要为其保持写锁定的操作。因此,由于调用者不一定持有该锁,所以我在函数的开头获取了该锁,并在该函数的末尾释放了该锁。do_extra_dangerous_stuff()
调用do_dangerous_stuff()
。但是,在调用之前,它已经执行了还需要写锁定的操作,并且直到对do_dangerous_stuff()
的调用返回之前,数据才是不一致的(因此释放并立即重新获取该锁定可能会破坏事情)。实际上,这要复杂得多。可能有一堆调用do_dangerous_stuff()
的函数,要求每个函数在调用do_dangerous_stuff()
之前获取锁可能是不切实际的。有时,锁是在一个功能中获取的,而在另一功能中释放。
使用读取锁,只要确保释放与获取的锁数量相同的锁,我就可以从同一线程多次获取它。对于写锁,这不是一个选项(尝试这样做将导致死锁)。一个简单的解决方案是:测试当前线程是否已经持有该锁,如果不是,则获取该锁,反之,测试当前线程是否仍然持有该锁,如果存在则释放它。但是,这需要我测试当前线程是否已持有该锁-有没有办法做到这一点?
答案 0 :(得分:1)
看着pthread_rwlock_wrlock()
的{{3}},我看到它说:
如果成功,
pthread_rwlock_wrlock()
函数应返回零;否则,将返回错误编号以指示错误。[…]
在以下情况下,
pthread_rwlock_wrlock()
函数可能会失败:
EDEADLK
当前线程已经拥有用于写入或读取的读写锁。
在我读到它时,EDEADLK
从未用来表示涉及多个线程的链条,这些线程正在等待彼此的资源(从我的观察来看,这种死锁的确似乎导致了冻结,而不是EDEADLK
) 。似乎专门表明该线程正在请求当前线程已经拥有的资源,这是我要测试的条件。
如果我对文档有误解,请告诉我。否则,解决方案是简单地致电pthread_rwlock_wrlock()
。应该发生以下情况之一:
EDEADLK
,因为我们已经持有了锁。无需重新获取,但我们在释放锁时可能要考虑这一点,这取决于所讨论的代码,请参见下文跟踪我们获得锁并获得EDEADLK
的次数可能是有意义的。从吉尔·汉密尔顿(Gil Hamilton)的答案中,锁定深度将对我们有用:
EDEADLK
时,将锁定深度增加1。这应该是线程安全的,无需进一步同步,因为锁定深度受到它所引用的锁定的有效保护(我们仅在按住锁定时触摸它)。
注意::如果当前线程已持有读取锁(其他人没有),这也将其报告为“已锁定”。需要进一步测试以确定当前持有的锁是否确实是写锁。如果有多个线程(其中有当前线程)持有读取锁,我不知道尝试获取写锁是否会返回EDEADLK
或冻结该线程。这部分需要更多的工作...
答案 1 :(得分:0)
AFAIK,没有简单的方法可以完成您想做的事情。
在linux中,您可以使用“递归”互斥属性来实现您的目的(例如,此处显示为https://stackoverflow.com/a/7963765/1076479),但这不是“ posix”可移植的。
唯一真正可移植的解决方案是推出自己的同类产品。为此,您可以创建一个包含锁以及自己的线程索引(或等效线程)和所有权/递归计数的数据结构。
注意:伪代码掉在我头上
递归锁:
List<RadioButton> radioButtons = new ArrayList<RadioButton>();
radioButtons.add( (RadioButton)findViewById(R.id.Pradio1) );
radioButtons.add( (RadioButton)findViewById(R.id.Pradio2) );
radioButtons.add( (RadioButton)findViewById(R.id.Pradio3) );
for (RadioButton button : radioButtons){
button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) processRadioButtonClick(buttonView);
}
});
}
private void processRadioButtonClick(CompoundButton buttonView){
for (RadioButton button : radioButtons){
if (button != buttonView ) button.setChecked(false);
}
}
在出路时,它更简单,因为您知道自己拥有该锁,因此只需确定是否应该释放它(即最后一次解锁)。递归解锁:
// First try to acquire the lock without blocking...
if ((err = pthread_mutex_trylock(&mystruct.mutex)) == 0) {
// Acquire succeeded. I now own the lock.
// (I either just acquired it or already owned it)
assert(mystruct.owner == my_thread_index || mystruct.lock_depth == 0);
mystruct.owner = my_thread_index;
++mystruct.lock_depth;
} else if (mystruct.owner == my_thread_index) {
assert(err == EBUSY);
// I already owned the lock. Now one level deeper
++mystruct.lock_depth;
} else {
// I don't own the lock: block waiting for it.
pthread_mutex_lock(&mystruct.mutex);
assert(mystruct.lock_depth == 0);
}
我也想在信任之前添加一些其他检查和断言以及重要的测试。