pthreads:读/写锁,升级读锁到写锁

时间:2010-03-09 08:28:43

标签: c++ locking pthreads

我在Linux上使用读/写锁,我发现尝试将读锁定对象升级为写锁死锁。

// acquire the read lock in thread 1.
pthread_rwlock_rdlock( &lock );

// make a decision to upgrade the lock in threads 1.
pthread_rwlock_wrlock( &lock ); // this deadlocks as already hold read lock.

我已经阅读了手册页,它非常具体。

  

如果在,调用线程可能会死锁   打电话的时间是持有的   读写锁(无论是读还是读)   写锁定。。

在这些情况下,将读锁升级到写锁的最佳方法是什么。我不想在我正在保护的变量上引入竞争。

据推测,我可以创建另一个互斥锁来包含释放读锁定和获取写锁定但是我实际上并没有看到使用读/写锁定。我不妨简单地使用普通的互斥锁。

THX

4 个答案:

答案 0 :(得分:16)

在以下情况中你还想要一个死锁吗?

  • 线程1获取读锁定
  • 线程2获取读锁
  • 线程1要求升级锁定以写入
  • 线程2要求升级锁定以写入

所以我只是释放读锁定,获取写锁定并再次检查我是否要进行更新。

答案 1 :(得分:1)

最简单,最安全的方法是从您想要更改数据的那一刻开始进行写锁定,而不是从您确定要更改数据的那一刻开始。我知道这会使您的数据访问更加序列化。

在阅读这个问题时我有点意外,因为我从来没有考虑过首先采用读锁定然后升级到写锁定。那么,不同的情况可能需要不同的方法。

答案 2 :(得分:0)

我认为不使用pthread读/写锁,而是使用Posix fcntl()。在这里,您可以毫无困难地从读取升级到写入。我们正在使用它来插入B树。一旦我们知道插入发生的节点,我们就会将其升级为写锁定。此外,当我们需要拆分节点时,我们将升级节点的锁定,其父节点和子节点从读取到写入。由于B-tree是基于文件的数据结构,因此有助于锁定文件区域。

答案 3 :(得分:0)

pthread库不直接支持此操作。

作为一种解决方法,您可以定义一个互斥锁来保护锁:

  • 要获取锁,请首先获取互斥锁,然后获取锁(根据需要读取或写入),然后释放互斥锁。 (切勿在没有持有互斥锁的情况下获得锁。)
  • 要释放锁,只需释放它(此处不需要互斥锁)。
  • 要升级锁,请获取互斥锁,释放读取锁,获取写锁,然后释放互斥锁。
  • 要降级该锁,请获取互斥锁,释放写锁,获取读锁,然后释放互斥锁。

这样,在尝试升级写锁时,没有其他线程可以抢夺写锁。但是,在尝试升级时,如果其他线程持有读取锁,则您的线程将阻塞。

此外,如上所述,如果两个线程试图同时升级同一锁,则会遇到死锁:

  • T1和T2都拥有读锁。
  • T1希望升级,获取互斥锁,释放读取锁并尝试获取写入锁。这被T2阻止了。
  • T2希望升级,尝试获取互斥锁,并被T1阻止。

从我的CS讲座中删除:无法可靠地避免死锁。对于提出的每种策略,至少有一个用例不可行。您唯一可以做的就是检测死锁条件(即,如果调用失败,EDEADLK),并确保您的代码已准备好应对这种情况。 (如何恢复在很大程度上取决于您的代码。)

尽管降级和并发升级可能会导致死锁,但以这种方式降级不容易出现死锁¹。如果只有您的一个线程升级以这种方式锁定(并且其他线程在需要时立即获得写锁定),那么也就没有死锁的风险¹。

正如其他人所说,在您可能需要时立即获得写锁定将是不容易死锁的一种选择,但是可能不必要地阻止其他读操作同时发生。

>

结论:这取决于您的代码。

如果只读阶段很简短(即足够简短,因此您可以在这段时间内阻止其他读取操作),那么我会选择免费的写锁定方法。

如果只读阶段可能持续很长时间,并且在此期间阻止其他读取是不可接受的,请进行互斥锁保护的锁升级,但是将其限制为每个锁一个线程(“只有T1可以升级锁L42,但是而不是其他线程”)或提供一种检测并从死锁中恢复的方法。


¹除非该锁及其互斥锁以外的资源都可发挥作用,否则将无法使用