在阅读了测试和设置Wikipedia entry后,我仍然留下了“测试和设置将用于什么?”的问题。
我意识到您可以使用它来实现Mutex(如维基百科中所述),但它有什么其他用途?
答案 0 :(得分:14)
一个很好的例子就是“增量”。
假设两个线程执行a = a + 1
。说a
以值100
开头。如果两个线程同时运行(多核),则两者都将a
加载为100
,递增到101
,并将其存储回a
。错!
通过测试和设置,您说“将a
设置为101
,但前提是它当前的值为100
。”在这种情况下,一个线程将通过该测试,但另一个线程将失败。在失败的情况下,线程可以重试整个语句,这次加载a
为101
。成功。
这通常比使用互斥锁更快,因为:
答案 1 :(得分:8)
您可以在执行某些工作后随时将数据写入内存,并确保自启动以来另一个线程未覆盖目标。很多lock/mutex-free algorithms采取这种形式。
答案 2 :(得分:5)
想象一下,您正在编写银行应用程序,并且您的应用程序要求从帐户中提取10英镑(是的,我是英语;))。因此,您需要将当前帐户余额读入本地变量,减去提款,然后将余额写回内存。
但是,如果您在读取值并将其写出之间发生另一个并发请求,该怎么办?该请求的结果可能会被第一个请求完全覆盖,并且帐户余额将不正确。
测试和设置通过检查您的覆盖值是否符合您认为应该是的值来帮助我们解决该问题。在这种情况下,您可以检查余额是否是您阅读的原始值。因为它是原子的,所以它是不可中断的,所以没有人可以在读取和写入之间从你身下拉出地毯。
解决同一问题的另一种方法是取消内存位置的锁定。不幸的是,锁定非常难以正确,难以推理,存在可扩展性问题并且在面对失败时表现不佳,因此它们不是理想的(但绝对实用的)解决方案。测试和设置方法构成了一些软件事务存储器的基础,它可以乐观地允许每个事务同时执行,但如果它们发生冲突则会将它们全部回滚。
答案 3 :(得分:1)
基本上,考虑到原子性的巨大重要性,它的使用完全适用于互斥体。就是这样。
测试和设置是一种可以使用其他两个指令执行的操作,非原子和更快(在多处理器系统上,原子性会带来硬件开销),因此通常不会出于其他原因使用它。 / p>
答案 4 :(得分:0)
如果您需要获取共享值,使用它执行某些操作并更改值,则使用它,假设另一个线程尚未更改它。
至于实际用途,我最后一次看到它是在并发队列的实现中(可以由多个线程推送/弹出的队列,而不需要信号量或互斥量)。
为什么要使用TestAndSet而不是互斥锁?因为它通常比互斥锁需要更少的开销。在互斥锁需要OS干预的情况下,TestAndSet可以作为CPU上的单个原子指令实现。在具有100个线程的并行环境中运行时,代码的关键部分中的单个互斥锁可能会导致严重的瓶颈。