在多线程C++代码中,我需要使一些非常快的操作原子化(看起来是序列化的),这样我就可以使用自旋锁,例如:
lock mutex: while (lock.test_and_set(std::memory_order_acquire))
unlock mutex: lock.clear(std::memory_order_release);不过,我认为自己很聪明,并将锁定以数据结构当前是否由多个线程共享为条件:
lock mutex: if(lockneeded) while (lock.test_and_set(std::memory_order_acquire))
unlock mutex: if(lockneeded)lock.clear(std::memory_order_release);最初,数据结构只由一个线程拥有,但是所有者可以授予它对另一个线程的访问权,此时它必须设置锁所需的变量(必须是原子布尔本身)。
这个能行吗?
编辑:一些上下文。我有一个调度协同的系统。挂起的协同队列由单个线程一次运行一个,直到挂起或完成为止,然后再运行下一个队列。这个系统最初是为单个线程设计的,因为规范上的协同是顺序编程结构。上下文切换时间非常快,因为协同器使用堆分配的链接列表,而不是机器堆栈。因此,上下文切换基本上只是一个指针交换。
然后,我决定允许多个线程处理列表,这样协同就变成了进程。现在指针交换必须以原子方式完成。掉期非常快,因此自旋锁似乎是保护操作的正确方法。
我有一个测试用例,在这里我依次运行一组作业,然后再用额外的辅助线程执行它。我遇到了一个问题,现在我已经解决了,结果证明这个问题与日程安排无关。现在,4个线程运行进程的速度是1的3.5倍。
表演的目标很简单:我想抹去地球上的古朗。我的系统符合C/C++ ABI (Go不是),它使用正确的流处理模型(Go不),它也是一种非常优秀的语言。
我不知道上下文切换的速度有多快。但是,我的测试用例的当前未调优版本是在5秒内处理200万个进程,这是一个大约每秒400 K的上下文切换速率,在这个版本中,我们不能忘记作业计数到100 K以造成延迟(并确保锁上的争用几乎为零)。我预计,如果我用空作业(不做任何协同工作)替换慢作业,速度将超过每秒1000次。正在运行200万个进程。现实世界的速度会较低,实验是试图找出性能的上限。
发布于 2018-12-05 21:04:58
不,不幸的是这行不通。
假设线程A看到lockneeded是假的,在没有获取lock的情况下进入关键部分,然后在关键部分的中间发生上下文切换。线程B请求访问数据结构。数据结构不知道线程A在关键部分,因此线程B被授予访问权限。lockneeded设置为true,但线程A已经在其关键部分中。线程B然后获得lock..。您可以很容易地看到这是未定义的行为。
除非您能够保证lockneeded在关键部分不会更改,否则它就无法工作。保证lockneeded不会改变的一种方法是使用一个锁来保护它。因此,您需要为lockneeded的每一个访问添加一个锁,因此首先就会忽略变量的用途。
高效C++自旋锁
自旋锁在概念上是如此简单,但有许多口味可用。需要考虑的重要因素是性能要求(是否真的需要如此高效?)、体系结构、线程库、所需的可伸缩性、预期争用量(如果争用很少,则可以针对非争用情况进行优化)、使用相同锁的关键部分的不对称性(以防止线程饥饿)、读写比.你可以看到,如果你需要它是超级高效,有很多性能测试,你需要做。所以,如果你真的不需要你的表现,你应该使用你的自旋锁,把你的时间花在其他地方。
但我们是计算机科学家,我们喜欢最有效的解决方案,因为我们是问题的解决者。对于一个高度争议,高度可伸缩的自旋锁,请查看MCS锁。对于一般良好的自旋锁,我在不久前运行了一些测试,发现线程的自旋锁是相当可伸缩的。
还有一种方法可以保证线程A在没有线程A必须写任何东西的情况下不会出现在关键部分。它被称为rcu_synchronize,为了非常简化,它将涉及线程B设置lockneeded,并等待足够的时间来保证关键部分中的任何线程都会完成它。
由于锁变量的缓存丢失(全局写入使其他正在旋转的内核无效),朴素自旋锁的比例很低,因为总线流量很低。
您可以做的一个简单优化是“自旋读”自旋锁:
lock mutex: while (lock.load(std::memory_order_acquire) || lock.test_and_set(std::memory_order_acquire)) {}
unlock mutex: no change因此,如果另一个线程有锁,则此线程不会使用TSL (由于或短路),但是当另一个线程释放锁时,该线程尝试TSL,这可能成功,也可能不成功。不幸的是,此锁在高扩展场景中的性能与朴素自旋锁一样糟糕,但在低比例、中度争用的情况下,它可能会不时地比朴素自旋锁节省一些周期。
https://stackoverflow.com/questions/53586329
复制相似问题