为什么库在Windows上实现自己的基本锁?

时间:2009-08-10 17:35:32

标签: c++ windows multithreading synchronization locking

Windows提供了许多用于同步线程的对象,例如事件(包括SetEventWaitForSingleObject),互斥锁和关键部分。

我个人总是使用它们,特别是关键部分,因为我很确定它们会产生非常小的开销,除非已经锁定。但是,考虑到一些库,比如boost,人们在Windows上使用互锁方法实现自己的锁会遇到很多麻烦。

我可以理解为什么人们会编写无锁队列等等,因为那是一个专门的案例,但人们有没有理由选择实现自己版本的基本同步对象?

5 个答案:

答案 0 :(得分:14)

库没有实现自己的锁。没有OS支持,这几乎是不可能的。

他们 所做的只是简单地包装操作系统提供的锁定机制。

Boost之所以这样做有几个原因:

  • 他们能够利用C ++功能提供更好的锁定API。 Windows API仅限C,而且设计不是很好。
  • 他们能够提供一定程度的便携性。如果您在Linux计算机或Mac上运行应用程序,则可以使用相同的Boost API。 Windows自己的API显然是特定于Windows的。
  • Windows提供的机制有一个明显的缺点:它们要求您包含windows.h,您可能希望避免出于多种原因,尤其是它的极端宏滥用会污染全局命名空间。

答案 1 :(得分:4)

我能想到的一个特殊原因是便携性。 Windows锁本身就好了,但它们不能移植到其他平台。希望可移植的库必须实现自己的锁,以保证跨平台的语义相同。

答案 2 :(得分:2)

  1. 在许多库(又名Boost)中,您需要编写corss平台代码。因此,使用WaitForSingleObject和SetEvent是不行的。此外,还有常见的习惯用法,如监视器,Win32 API未命中的条件,(但可以使用这些基本原语实现)
  2. 像原子计数器这样的无锁数据结构非常有用;例如:boost::shared_ptr使用它们以使其线程安全而不会产生关键部分的开销,大多数编译器(而不是msvc)使用原子计数器来实现线程安全的写时复制std::string。 / LI>
  3. 像队列这样的东西可以非常有效地以线程安全的方式实现,而不需要任何锁定,这可能会在某些应用程序中提供显着的性能提升。

答案 3 :(得分:2)

有时可能有充分的理由来实现不使用Windows操作系统同步对象的锁。但这样做是一个“尖锐的棍子”。很容易在脚下戳自己。

以下是一个示例:如果您知道自己运行的线程数量与硬件上下文相同,并且唤醒其中一个等待锁定的线程的延迟对您来说非常重要,那么您可能会选择完全在用户空间中实现的旋转锁定。如果等待线程是锁上旋转的唯一线程,则将锁从拥有它的线程转移到等待线程的延迟只是将缓存行移动到所有者线程并返回到等待线程的延迟 - 比在相同情况下用OS锁定信号的线程的延迟快几个数量级。

但是你想要这样做的场景非常狭窄。一旦你开始拥有比硬件线程更多的软件线程,你可能会后悔。在那种情况下,你可以花费整个操作系统调度量,除了旋转自旋锁之外什么都不做。而且,如果您关心电源,螺旋锁是不好的,因为它们会阻止处理器进入低功耗状态。

我不确定我是否购买了便携性论点。可移植库通常具有OS可移植层,该层抽象不同的OS API以进行同步。如果您正在处理锁定,则pthread_mutex可以在语义上与抽象层下的Windows Mutex或Critical Section相同。这里有一些例外,但对大多数人来说这是事实。如果您正在处理Windows事件或POSIX条件变量,那么那些更难以抽象。 (Vista确实引入了POSIX风格的条件变量,但没有多少Windows软件开发人员能够要求Vista ......)

答案 4 :(得分:1)

如果该库是跨平台的,则编写库的锁定代码非常有用。库的用户可以使用库的锁定功能,而不必关心底层平台实现。假设该库具有针对所有目标平台的版本,则需要移植一小部分代码。

相关问题