有一个名为flock()
的Unix函数,进程可以使用它来获取对资源的共享(“读”)访问或独占(“写”)访问。问题是它会使那些请求独占访问的进程匮乏。这样的请求保持排队,直到没有进程持有共享锁;同时,新的共享锁请求被“授权”在等待独占锁定的进程之前。
显然,请求共享锁的进程越多,编写器就必须等待没有未完成的共享锁的偶然时间窗口。
我寻求的行为是这样的:一旦作家请求了一个独占锁,那么请求共享锁的后续读者将排在作者后面。这种锁的名称,我' m告诉,是“作家喜欢读/写锁”。
有几个帖子(特别是this one)解决了这个问题,但是在线程级别。我需要的是面向Unix / Linux的解决方案,以这种方式协调进程。
更新:我需要解决方案来处理参与进程在锁定时可能会崩溃的可能性,方法是自动删除锁定。
答案 0 :(得分:2)
您可以使用referenced question中描述的方法进行进程间和线程间同步。您必须确保pthread_rwlock_t对象在要同步的进程之间共享的内存中,并使用pthread_rwlockattr_setpshared()
函数将用于初始化pthread_rwlock_t的pthread_rwlockattr_t对象标记为PTHREAD_PROCESS_SHARED。
如果在进程退出时需要同步来重置自身,则需要使用基于不同同步原语的读写器锁。我认为System V semaphores (otherwise known as XIS IPC semaphores)应该可以解决问题,因为当操作它们的进程退出而不重置它们时,它们应该重置自己。 System V信号量是Linux内核的可选功能。
您可能希望使用具有更高级别锁定抽象的库 - 如果您使用的是C ++,则可以使用boost synchronization。但是,我不确定boost同步是否支持System V IPC信号量支持的锁定 - 您可能被迫在Sys V信号量集之上滚动自己的读写器锁定。具体来说,您需要读取信号量,写入信号量和写入队列信号量。写入器递增写入队列并等待读取和写入信号量变为0,然后递增写入信号量。读者等待写入队列并将信号量写入0,然后递增读取信号量。
答案 1 :(得分:0)
我迟到了,但现在只有类似的任务,发现可能的简单解决方案,这是强大的(即如果锁的所有者崩溃,那么系统会自动释放锁):使用double flock()打电话给不同的目标。假设两个任意锁文件已经打开到描述符fd_sh和fd_ex中。然后获得共享访问权限:
获得独家访问权限:
这种方法为编写者提供了更多获取锁定的机会,因为读者只需要fd_ex非常小的时间来锁定共享模式中的fd_sh,而在没有工作编写器的情况下反过来非常快。因此,第一作者将在相当短的时间内完成步骤1,并且在步骤2中将仅等待已经拥有锁定的读者。当一个作者正在工作时,所有读者和其他作者都处于相同的状态,只有内核决定,哪一个会接受下一次锁定,但是下一个作者不需要等待所有读者(哪个内核放在队列前面)服务员)将完成他们的工作,只有很短的时间通过步骤1-3,所有读者将同时通过(当然,如果有足够的核心)。
如果有人因持有锁而崩溃,则会以无声方式释放锁定。必须特别注意检测其他工人的这种情况。例如,如果只有编写器的崩溃有意义,那么在编写器中的某些关键工作开始时,某些标记可以放在fd_ex描述符下的文件中,并在解锁之前清除。读者可以检查该标记并跳过工作或启动重新检查。下一位作家可以完成这项工作。
对三种实现进行了一些综合测试:单个鸡群,建议的双鸡群和pthread_rwlock(使用PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP attr)。 我使用了8个进程(用于pthread_rwlock的线程),三个读/写比率变体,两个测试平台。
测试平台1的结果 - Debian Linux上的双核Intel core2,内核3.16.36:
90% reads/10% writes
single flock double flock pthread_rwlock
total overhead* 90.4% 21.7% 11.6%
readers waittime* 20.2% 50.1% 47.1%
writers waittime 95.2% 64.5% 54.8%
50% reads/50% writes
single flock double flock pthread_rwlock
total overhead 22.0% 33.7% 3.2%
readers waittime 63.6% 82.2% 82.7%
writers waittime 87.8% 84.0% 70.3%
10% reads/90% writes
single flock double flock pthread_rwlock
total overhead 5.3% 8.4% 0.2%
readers waittime 82.5% 87.2% 96.8%
writers waittime 87.3% 87.4% 78.5%
总开销'是比率('实际处理时间' - '理想时间')/'理想时间'理想时间是所有有用的共享工作由所有人同时均匀完成工人和所有有用的专属工作由单个工人完成或由几个工人按顺序完成;
' WAITTIME'比率是等待锁定的时间' /('等待锁定的时间' +'有用的工作'),因为理想值不为零并且取决于读写比率和工作人员数量,所以信息量不大;
使用内核3.19.6在Debian Linux上测试平台2 - 16个真核(32个带HT,Intel Xeon)的结果:
90% reads/10% writes
single flock double flock pthread_rwlock
total overhead 134.3% 17.1% 11.6%
readers waittime 13.2% 46.4% 45.8%
writers waittime 96.7% 65.3% 54.3%
50% reads/50% writes
single flock double flock pthread_rwlock
total overhead 37.9% 30.5% 2.9%
readers waittime 46.1% 78.4% 83.1%
writers waittime 90.5% 85.9% 70.0%
10% reads/90% writes
single flock double flock pthread_rwlock
total overhead 7.2% 9.0% 0.4%
readers waittime 66.9% 80.6% 96.8%
writers waittime 88.0% 87.9% 78.4%
正如您所看到的,与单个鸡群相比,建议的双鸡群方法可以显着降低低写入率的开销。在所有情况下,高写入比率开销相当低。对于相同的案例结果取决于测试平台。当所有工作人员的CPU数量足够时,争用率会高得多。 Pthread的rwlock在所有情况下都表现出非常好的结果。所以,如果可以的话,请使用它,但要记住,在工人粗暴死亡的情况下,锁定不会被释放。
关于测试方法的更多信息。读者和作者的有用工作由" usleep(10000 + rand()%1000)"调用。使用clock_gettime(CLOCK_MONOTONIC)计算等待和有用工作的实时。每次迭代后(锁定释放后)都有额外的usleep(1)调用,以使争用更接近等待新请求到达的现实应用程序。如果没有这个调用,两种群集方法的结果都会在多核平台上大幅下降。