编写满足无锁进度保证的算法或数据结构的困难之一是动态内存分配:调用类似malloc
或new
之类的内容并不保证是无锁定的。便携式。但是,存在malloc
或new
的许多无锁实现,并且还有各种无锁内存分配器可用于实现无锁算法/数据结构。
但是,我仍然不明白这实际上如何完全满足无锁进度保证,除非您专门将数据结构或算法限制为某些预先分配的静态内存池。但是如果你需要动态内存分配,我不明白任何所谓的无锁内存分配器在长期内如何真正无锁。问题在于,无论你的无锁malloc
或new
多么惊人,最终你可能会耗尽内存,此时你不得不再向操作系统询问更多内存。这意味着最终你必须调用brk()
或mmap()
或一些这样的低级别等效物来实际访问更多内存。并且无法保证任何这些低级别调用都是以无锁方式实现的。
根本无法解决这个问题,(除非您使用的是像MS-DOS这样不提供内存保护的古老操作系统,或者您编写自己完全无锁的操作系统 - 两种不实用的方案或可能。)那么,任何动态内存分配器如何真正无锁?
答案 0 :(得分:3)
正如你自己发现的那样,基本的OS分配器很可能不是无锁的,因为它必须处理多个进程和各种有趣的东西,这使得很难不引入某种锁。
但在某些情况下,"锁定可用内存分配"并不代表"永远不会锁定",但"统计上锁定很少,以至于它并不重要"。除了最严格的实时系统之外,哪种方法都适用。如果你的锁没有很高的争用,那么锁定或没有锁定并不重要 - 锁定的目的实际上并不是锁本身的开销,而是锁定的容易程度一个瓶颈,系统中的每个线程或进程必须通过这一个地方做任何有用的事情,并且当它这样做时,它必须在队列中等待[它可能不是真正的队列,它可能是& #34;谁先醒来"或其他一些机制,在当前来电者之后决定下一个出来的人。
有几种不同的选择可以解决这个问题:
如果您有一个有限大小的内存池,您可以在启动软件时立即向操作系统询问所有内存。在内存从操作系统中分离出来之后,它可以用作无锁池。明显的缺点是它可以分配多少内存。然后,您必须停止分配(全部使应用程序失败,或者使该特定操作失败)。
当然,在像Linux或Windows这样的系统中,仍然无法保证在无锁方案中的内存分配意味着即时访问已分配的内存",因为系统可以和将在没有实际物理内存支持的情况下分配内存,并且只有在实际使用内存时,才会为其分配物理内存页面。这可能都涉及锁定,例如磁盘I / O将其他页面分页到交换。
对于这样严格的实时系统,单个系统调用的时间可能会导致锁定太多",解决方案当然是使用专用操作系统,一个在操作系统内部有一个无锁分配器(或者至少有一个具有可接受的已知实时行为的分配器 - 它最多锁定X个微型扫描[X可以小于1.0])。实时系统通常有一个内存池和固定大小的存储桶,用于回收旧的分配,这可以无锁的方式完成 - 存储桶是一个链表,因此您可以使用原子比较&列表从列表中插入/删除。交换操作[可能需要重试,因此虽然技术上没有锁定,但在竞争情况下等待时间并非为零]。
另一个可行的解决方案是每个线程池""。如果你在线程之间传递数据,这会有点复杂,但是如果你接受那个"释放的内存以便重用可能最终会出现在另一个线程中。 (这当然导致了问题,并且#34;所有内存现在都位于一个线程中,该线程收集并释放来自许多其他线程的信息,并且所有其他线程已经耗尽内存")