在阅读了测试和设置维基百科条目后,我仍然留下了"测试和设置将用于什么?"的问题.
我意识到你可以使用它来实现Mutex(如维基百科中所述),但它有什么其他用途?
一个很好的例子就是"增量".
说两个线程执行a = a + 1
.说a
从值开始100
.如果两个线程同时运行(多核),则两者都将加载a
为100
,递增101
并将其存储回来a
.错误!
通过测试和设置,您说"设置a
为101
,但仅当它具有当前值时" 100
.在这种情况下,一个线程将通过该测试,但另一个线程将失败.在失败的情况下,线程可以重试整个语句,这次加载a
为101
.成功.
这通常比使用互斥锁更快,因为:
大多数情况下没有竞争条件,所以更新发生时无需获取某种互斥量.
即使在碰撞过程中,一个线程根本就不会被阻塞,而另一个线程旋转和重试的速度要快于为某些互斥锁暂停自身的速度.
您可以在执行某些操作后随时将数据写入内存,并确保自启动以来另一个线程未覆盖目标.许多锁定/互斥锁算法采用这种形式.
想象一下,您正在编写银行应用程序,并且您的应用程序要求从帐户中提取10英镑(是的,我是英语;)).因此,您需要将当前帐户余额读入本地变量,减去提款,然后将余额写回内存.
但是,如果您在读取值并将其写出之间发生另一个并发请求,该怎么办?该请求的结果可能会被第一个完全覆盖,并且帐户余额将不正确.
测试和设置通过检查您的覆盖值是否符合您认为应该是的值来帮助我们解决该问题.在这种情况下,您可以检查余额是否是您阅读的原始值.因为它是原子的,所以它是不可中断的,所以没有人可以在读取和写入之间从你身下拉出地毯.
解决同一问题的另一种方法是取消内存位置的锁定.不幸的是,锁定非常难以正确,难以推理,存在可扩展性问题并且在面对失败时表现不佳,因此它们不是理想的(但绝对实用的)解决方案.测试和设置方法构成了一些软件事务存储器的基础,它们乐观地允许每个事务同时执行,但是如果它们发生冲突则以将它们全部回滚为代价.