Synchronization 测试和设置用于什么?

Synchronization 测试和设置用于什么?,synchronization,embedded,mutex,test-and-set,Synchronization,Embedded,Mutex,Test And Set,在阅读了测试和设置之后,我仍然有一个问题“测试和设置的用途是什么?” 我知道你可以用它来实现互斥体(如维基百科所述),但是它还有什么其他用途呢?基本上,考虑到原子性的巨大重要性,它的用途正是用于互斥体。就这样 测试和设置是一种操作,可以使用其他两条指令执行,非原子指令和更快的指令(在多处理器系统上,原子性承担硬件开销),因此,通常情况下,您不会出于其他原因使用它。您可以在执行某些工作后,在任何时候将数据写入内存,并确保自启动后,其他线程没有覆盖目标。很多人都采取这种形式。一个很好的例子是“增量”

在阅读了测试和设置之后,我仍然有一个问题“测试和设置的用途是什么?”


我知道你可以用它来实现互斥体(如维基百科所述),但是它还有什么其他用途呢?

基本上,考虑到原子性的巨大重要性,它的用途正是用于互斥体。就这样


测试和设置是一种操作,可以使用其他两条指令执行,非原子指令和更快的指令(在多处理器系统上,原子性承担硬件开销),因此,通常情况下,您不会出于其他原因使用它。

您可以在执行某些工作后,在任何时候将数据写入内存,并确保自启动后,其他线程没有覆盖目标。很多人都采取这种形式。

一个很好的例子是“增量”。

假设两个线程执行
a=a+1
。假设
a
以值
100
开始。如果两个线程同时运行(多核),则两个线程都会将
a
加载为
100
,增量为
101
,并将其存储回
a
。错了

使用test和set时,您会说“将
a
设置为
101
,但只有当它当前的值为
100
”在这种情况下,一个线程将通过该测试,而另一个线程将失败。在失败的情况下,线程可以重试整个语句,这次将
a
加载为
101
。成功

这通常比使用互斥锁快,因为:

  • 大多数情况下没有竞争条件,因此更新不需要获取某种互斥
  • 即使在冲突期间,一个线程也不会被阻塞,而另一个线程旋转并重试的速度要快于将自身挂起以获得互斥锁的速度

  • 当您需要获取共享值、使用它做一些事情并更改该值(假设其他线程尚未更改该值)时,可以使用它

    至于实际用途,我最后一次看到它是在并发队列(可以由多个线程推送/弹出而不需要信号量或互斥量的队列)的实现中


    为什么要使用TestAndSet而不是互斥?因为它通常比互斥体需要更少的开销。当互斥体需要操作系统干预时,TestAndSet可以作为CPU上的单个原子指令来实现。在具有100个线程的并行环境中运行时,关键代码段中的单个互斥可以导致严重的瓶颈。

    想象您正在编写一个银行应用程序,并且您的应用程序请求从帐户中提取10英镑(是的,我是英国人;))。因此,您需要将经常账户余额读入一个局部变量,减去取款金额,然后将余额写回内存

    但是,如果在读取值和写入值之间发生另一个并发请求,该怎么办?有可能该请求的结果将被第一个请求完全覆盖,并且帐户余额将不正确

    Test and set通过检查覆盖的值是否与您认为的值一致,帮助我们解决该问题。在这种情况下,您可以检查余额是否为您读取的原始值。因为它是原子的,所以它是不可中断的,所以没有人可以在读写之间从你的下面拉出地毯


    解决相同问题的另一种方法是取出内存位置的锁。不幸的是,锁非常难以正确使用,难以推理,存在可伸缩性问题,并且在遇到故障时表现糟糕,因此它们不是理想的(但绝对是实用的)解决方案。测试和设置方法构成了一些软件事务性存储器的基础,这些存储器乐观地允许每个事务同时执行,如果它们发生冲突,则以回滚所有事务为代价。

    @jason cohen:这实际上是一个对事务性存储器的描述。测试和设置通常只涉及值0和1。设置部分指将指定内存位置的值设置为1。它返回上一个值,1或0,并在单个原子操作中完成所有这些操作。@GregSlepak是正确的,这是比较和交换。test_and_set()获取指向目标的布尔指针,将其设置为TRUE并返回指针的原始值。如果test_和_set(&lock)的返回值(即&lock的原始值)为真,则我们进入临界部分。同意@GregSlepak的说法,上述回复是比较和交换的示例,而不是test-and-set。互斥体内部可能使用test&set。如何比较TestAndSet和mutex。我认为与Python相比,互斥对象显式地有一个“testandset”方法是不对的。文档将其称为“原子”,并加上引号,这并不能激发信心,但它确实表明这两个概念并不是互斥的。