繁体   English   中英

成千上万的读/写器锁在一个进程中

[英]Thousands of reader/writer locks in a single process

我目前正在设计一个具有大规模同步模式的C ++跨平台(Linux / Windows)服务器应用程序。 我在内部使用boost :: thread作为特定于操作系统的线程的抽象。 我的问题是保护数据数组,数组的每个元素都受独立的读/写锁保护

我的数组包含4096个元素 考虑到“ 信号量小书 ”(第85页)中提出的“作者优先级读者 - 作者”问题的解决方案,我的应用程序每个数组元素需要5个信号量。 这总共提供了大约20000个信号量(或者,相当于20000个互斥量+ 20000个条件变量)。

我的应用程序的另一个特点是在给定时间内,大多数信号量都不活动(通常有大约32个“客户端”线程在数千个信号量上等待/发送信号)。 请注意,由于整个服务器在单个进程中运行,因此我使用轻量级,基于线程的信号量( 不是进程间信号量)。

我的问题是双重的:

  1. 是否建议在Linux和Windows上为单个进程创建总计20000个信号量 嗯,当然,我想情况并非如此......

  2. 如果不推荐这种做法,我可以使用什么技术来减少实际信号量的数量,例如在1个实际信号量的顶部创建一组N个“模拟信号量” 我想这将是一个有趣的解决方案,因为我的大多数信号量在给定时间都处于非活动状态。

提前致谢!

到目前为止答案的摘要

  1. 建议不要使用数千个信号量,尤其是从跨平台的角度来看。 所以,即使它们不是进程间信号量(它们仍然在Windows下使用句柄)。
  2. 解决我的问题的直接方法是将我的数组拆分为例如64个16个元素的子数组,并将每个子数组与一个读/写锁相关联 不幸的是,这引入了很多争用(1个写入器会阻止读取到15个元素)。
  3. 深入研究Boost源代码,我发现:

    • “boost :: mutex”的实现不包装Windows下的CRITICAL_SECTION对象(但是CreateEvent和ReadWriteBarrier),
    • “boost :: shared_mutex”在Windows下使用CreateSemaphore(重量级,进程间对象),以及
    • “boost :: shared_mutex”不会在Linux下包装“pthread_rwlock_t”。

    其中的原因似乎并不清楚。 特别是,在Windows下使用“boost :: shared_mutex”的进程间对象对我来说似乎不是最优的。

迄今为止的公开问题摘要

  1. 如何在1个实际信号量的顶部创建一组N个“模拟信号量”,使模拟信号量之间的争用尽可能小?
  2. “boost :: mutex”和“boost :: shared_mutex”与其原生对应物(CRITICAL_SECTION和pthread_rwlock_t)相比如何?
  1. 不建议这样做。 实际上你不应该这样做,因为在Windows中它会消耗每个信号量1个句柄对象。 进程只能管理特定数量的Handles对象。 线程/进程和其他Windows对象可能需要使用Handle对象,如果不能,则会崩溃。 这在Linux中与文件描述符概念类似。

  2. 将4096个元素拆分为30个(例如)140个元素集,并为每个140个组分配一个信号量。 然后30(在此示例中)线程将尝试访问这30个集合,并且它们将基于每个140组信号量进行同步。

我会从Windows的角度告诉你我对它的看法。 我在为Windows编写服务器应用程序方面经验丰富。

首先,为单个进程创建20k信号量绝对没有问题。 它是一个非常轻量级的内核对象。 甚至是“进程间”信号量。

然而,我看到你的设计存在另一个问题。 您应该知道,您在内核对象上执行的每个操作(例如信号量/互斥锁)都涉及繁重的内核模式事务(也称为系统调用)。 即使没有任何冲突,每次这样的调用都可能花费你大约2k个CPU周期。

因此,您可能会发现自己只是在调用同步方法时花费了大部分处理器时间。

相反,为了同步线程,可以使用互锁操作。 它们的成本要低得多(通常是几十个CPU周期)。

还有一个叫做临界区的对象。 它是一种互锁操作数和内核对象的混合体(如果存在实际碰撞则使用它)。 您应该检查通常锁定元素的时间。 如果它通常是一个短持续时间的锁 - 只需使用关键部分,忘记复杂的读写锁。

但是,如果您处理长持续时间的锁,并且确实需要读写锁,并且您发现在内核模式事务中花费了大量CPU - 请考虑创建自己的(或尝试查找现有的)混合像这样的锁的实现。

在Linux上你绝对不应该自己实现锁,而是使用posix_rwlock_t

具有4096这样的元素的阵列不应该呈现任何特定问题。 POSIX锁结构在Linux中非常有效地实现。 特别是它们在可能的情况下在“快速路径”上使用原子操作,并且当该特定锁上存在拥塞时仅进入系统调用(特别是对于FUTEX)。 因此,如果您相对谨慎地实现,以便任何线程一次只能保存1个或2个锁,那么Linux上的约束只能由工作线程的总数而不是对象本身的数量给出。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM