0

情况:

  • 程序正在使用pthread_rwlock_t,比如说foolock
  • 一个线程,比如 T1,pthread_rwlock_wrlock()foolock
  • T1 尝试获取读锁 ( pthread_rwlock_rdlock()) onfoolock
  • 没有其他线程拥有读锁或写锁foolock
  • 有匹配的解锁。

预计会发生什么?

程序(特别是 T1)收到一个错误:

pthread_rwlock_rdlock() returns EDEADLK ("Resource deadlock avoided").

选择这种行为的动机是什么?如果授予读锁会出现什么问题?

什么可能是解决这种情况的好方法?或许,T1 需要维护一些它已经持有写锁的状态foolock。还有什么建议吗?

我的测试平台是Linux 2.6.32-431.11.2.el6.x86_64,NPTL 2.12

编辑1:很少澄清:

  • 我不是想将读锁升级为写锁
  • 我不是想将写锁降级为读锁
  • 我正在探索在已经获得写锁时是否可以授予读锁请求。

简化上下文:

  • 我正在尝试提供两个公共 API:(1)find()和 (2)update()
  • find()采用读锁
  • update()使用写锁
  • update()实现要调用 find() <<--问题

我目前的做法是:

  • 让每个公共API都有对应的无锁私有版本
  • 公共 API 执行 3 个步骤:

    • (a) 获得适当的锁,
    • (b) 调用私人版本,
    • (c) 释放锁
4

2 回答 2

3

POSIX 在 pthread_rwlock_rdlock() 中非常清楚:

如果在调用时调用线程持有写锁,则调用线程可能会死锁。

在 pthread_rwlock_wrlock() 中:

如果调用线程在调用时持有读写锁(无论是读锁还是写锁),则调用线程可能会死锁。

我怀疑这是为了让事情变得简单。

将读锁提升为写锁的能力看起来很有用,但(遗憾的是)它不是。考虑一个“索引”数据结构,其中“查找”自动添加未找到的值:当查找未找到给定值时,最好提升读锁,然后继续将值添加到索引在此期间没有任何改变的基础上。不幸的是,这行不通:考虑两个同时运行的查找,并且都需要写锁:-(因此,强制程序员放弃读锁并获取写锁至少可以很清楚什么正在发生。

请注意,如果“索引查找”有问题,如果它可以在它决定需要写锁的时候持有“n”个读锁,特别是如果它无法知道“n”是什么时:- (至少能够通过询问 rwlock 来发现 'n' 会很好!

顺便说一句,我发现 FreeBSD 有:

rw_try_upgrade(struct rwlock *rw)

尝试将单个共享锁升级为独占锁。当前线程必须持有 rw 的共享锁。这只有在当前线程持有 rw 上唯一的共享锁并且它只持有一个共享锁时才会成功。如果尝试成功,rw_try_upgrade() 将返回一个非零值,并且当前线程将持有一个排他锁。如果尝试失败,rw_try_upgrade() 将返回零,并且当前线程仍将持有共享锁。

...但我注意到它何时会真正做任何事情的所有警告!


关于将写锁降级为读锁的问题...

...如果没有挂起的写锁,语义就很清楚了。但是,如果有一个或多个待处理的 writer,事情就没有那么简单了。

目前,程序员被迫放弃写锁并获得读锁,很清楚发生了什么。

如果“降级”操作被定义为原子的“写解锁/读锁定”,那么这可以让读者在任何挂起的写者之前进入,或者只有在没有挂起的写者的情况下才能让读者进入——第二个让我觉得是更好的选择。“降级”的好处是在降级之后,人们知道状态没有再次改变——我想这很有用。

如果pthread_rwlock_rdlock()在持有写锁的同时被定义为“降级”,那么需要明确会导致多少级锁。如果结果是两个锁,释放第二个(读锁)是否会重新获得第一个(写锁)还是让它读锁?[如果可以将读锁“升级”为写锁,那么如何定义读锁/写锁/升级/降级/解锁的任意序列来工作?]

于 2014-07-10T09:50:38.743 回答
2

您已经一个读锁,因为写锁允许读取写入。

因此,您所要求的本质上是一个递归 rwlock,而没有提供一个的原因可能是递归锁都更复杂,并且通常首先表明实践不佳。

如果您真的无法重新编写代码以便它知道它持有什么锁,那么我同意您在某些特定于线程的结构中跟踪该状态的想法。与 C++ 中流行的 lock-guard 类型等效的东西会很合适。

正如 gmch 指出的那样,单独的读写升级案例还有许多其他问题。

于 2014-07-10T10:15:13.463 回答