C++ pthreads:读写器锁,将读锁升级为写锁

C++ pthreads:读写器锁,将读锁升级为写锁,c++,locking,pthreads,C++,Locking,Pthreads,我在Linux上使用读/写锁,我发现尝试将读锁定对象升级为写锁死锁 i、 e 我已经阅读了手册页,它非常具体 在以下情况下调用线程可能会死锁: 打电话时,它会保持 读写锁(无论是读还是写) 写入锁) 在这种情况下,将读锁升级为写锁的最佳方法是什么。。我不想在我保护的变量上引入竞争 大概我可以创建另一个互斥锁来包含读锁的释放和写锁的获取,但是我没有看到读/写锁的使用。我还不如简单地使用一个普通的互斥体 Thx最简单、最安全的方法是从您想要更改数据的那一刻起,而不是从您确定要更改数据的那一刻起,获取

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

i、 e

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

在以下情况下调用线程可能会死锁: 打电话时,它会保持 读写锁(无论是读还是写) 写入锁)

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

大概我可以创建另一个互斥锁来包含读锁的释放和写锁的获取,但是我没有看到读/写锁的使用。我还不如简单地使用一个普通的互斥体


Thx

最简单、最安全的方法是从您想要更改数据的那一刻起,而不是从您确定要更改数据的那一刻起,获取写锁。我知道这将使对数据的访问更加序列化


读到这个问题时我有点惊讶,因为我从来没有考虑过先使用读锁,然后升级到写锁。嗯,不同的情况可能需要不同的方法。

在下面的场景中,除了死锁之外,您还想要什么

  • 线程1获取读锁
  • 线程2获取读锁
  • 线程1请求将锁升级为写入
  • 线程2请求将锁升级为写入

因此,我只需释放读锁,获取写锁,然后再次检查是否必须进行更新。

我认为可以使用Posix fcntl()代替pthread read/write lock。在这里,您可以轻松地从读升级到写。我们使用它来插入B树。一旦我们知道发生插入的节点,我们将把它升级为写锁。此外,当我们需要拆分节点时,我们会将节点、其父节点和子节点的锁从读升级为写。由于B-tree是基于文件的数据结构,它有助于锁定文件区域。

pthread库不直接支持此操作

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

  • 要获取锁,首先获取互斥锁,然后获取锁(根据需要读取或写入),然后释放互斥锁。(未持有互斥锁,切勿获取锁。)
  • 要释放锁,只需释放它(此处不需要互斥)
  • 要升级锁,请获取互斥锁,释放读锁,获取写锁,然后释放互斥锁
  • 要降级锁,请获取互斥锁,释放写锁,获取读锁,然后释放互斥锁
这样,当您尝试升级写锁时,没有其他线程可以抢夺它。但是,当您尝试升级时,如果其他线程持有读锁,您的线程将被阻塞

另外,如上所述,如果两个线程同时尝试升级同一个锁,您将遇到死锁:

  • T1和T2都持有读锁
  • T1希望升级,获取互斥锁,释放读锁并尝试获取写锁。这被T2阻断了
  • T2希望升级,试图获取互斥锁,但被T1阻止
从我的CS讲座中可以看出:死锁无法可靠地避免。对于提出的每个策略,至少有一个用例中的策略是不切实际的。您唯一能做的就是检测死锁情况(即,如果使用
EDEADLK
调用失败),并确保您的代码准备好处理这种情况。(如何恢复在很大程度上取决于您的代码。)

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

正如其他人所说,在可能需要时立即获取写锁将是一种不容易出现死锁的替代方法,但可能会不必要地阻止其他读取操作同时发生

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

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

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



除非此锁及其互斥锁以外的资源发挥作用

如果另一个线程持有读锁,为什么其中一个线程会获得写锁?如果两者最终都能获得写锁,那么第二个问题是必须重新执行所有已进行的检查,因为受保护的资源可能已经发生了显著的更改。因此,释放和重新获取提供了相同的行为。我希望避免对被锁定的对象进行第二次检查。我知道IBM的读/写锁实现允许调用线程升级其锁(如果它是唯一持有写锁的线程)@不,是的,这会导致僵局。T1请求升级时将被阻止,因为T2具有读锁。当升级到写锁时,T2会阻塞,因为T1持有读锁。在回答您的问题“您期望什么”时,答案是,如果r/w锁是可升级的,则类似于:线程1尝试升级块,而线程2尝试失败。线程2对此做出响应,释放读卡器锁并尝试重新获取它,然后重新开始。一旦释放,线程1就可以获得writer锁,完成其事务并释放锁。有人不喜欢吗
// 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.