Language agnostic 原子指令

Language agnostic 原子指令,language-agnostic,synchronization,nonblocking,atomicity,test-and-set,Language Agnostic,Synchronization,Nonblocking,Atomicity,Test And Set,你说的原子指令是什么意思 以下内容是如何变成原子的 测试集 int TestAndSet(int *x){ register int temp = *x; *x = 1; return temp; } 从软件的角度来看,如果不想使用非阻塞同步原语,如何确保指令的原子性?是否只可能在硬件或某些汇编级指令优化可以使用?原子性只能由操作系统保证。操作系统使用底层处理器功能来实现这一点 因此,创建自己的testandset函数是不可能的。(虽然我不确定是否可以使用内联asm代码段,并

你说的原子指令是什么意思

以下内容是如何变成原子的

测试集

int TestAndSet(int *x){
   register int temp = *x;
   *x = 1;
   return temp;
}

从软件的角度来看,如果不想使用非阻塞同步原语,如何确保指令的原子性?是否只可能在硬件或某些汇编级指令优化可以使用?

原子性只能由操作系统保证。操作系统使用底层处理器功能来实现这一点

因此,创建自己的testandset函数是不可能的。(虽然我不确定是否可以使用内联asm代码段,并直接使用testandset助记符(可能是该语句只能通过操作系统权限完成))

编辑: 根据本文下面的评论,直接使用ASM指令生成您自己的“BiteStandset”函数是可能的(在英特尔x86上)。然而,这些技巧是否也适用于其他处理器尚不清楚


我坚持我的观点:如果你想做atmoic的事情,使用操作系统功能,而不是自己去做

一些机器指令本质上是原子的——例如,在许多体系结构上,读取和写入正确对齐的本机处理器字大小值是原子的

这意味着硬件中断,其他处理器和超线程不能中断读取或存储,也不能向同一位置读取或写入部分值

更复杂的事情,如读写一起原子可以通过显式的原子机器指令实现,例如x86上的LOCK CMPXCHG

锁定和其他高级构造构建在这些原子原语之上,这些原语通常只保护单个处理器字


一些聪明的并发算法可以通过读取和写入指针来构建,例如在单个读写器之间共享的链表中,或者通过努力在多个读写器之间共享。当您有任何形式的并行处理(包括不同的应用程序协作或共享数据)时,原子性是一个关键概念这包括共享资源

通过一个例子很好地说明了这个问题。假设您有两个程序想要创建一个文件,但前提是该文件不存在。这两个程序中的任何一个都可以在任何时间点创建文件

如果您这样做(我将使用C,因为它是您示例中的内容):

您无法确定另一个程序是否在“打开以供读取”和“打开以供写入”之间创建了文件


您自己无法做到这一点,您需要操作系统的帮助,操作系统通常为此提供同步原语,或者其他保证是原子的机制(例如,锁定操作是原子的关系数据库,或者处理器“测试和设置”指令等较低级别的机制)原子语来自希腊语ἄτομος(atomos),意思是“不可分割”。(注意:我不会说希腊语,所以可能它真的是另一回事,但大多数说英语的人引用词源来解释它。:-)

在计算中,这意味着操作发生了。在完成之前没有任何可见的中间状态。因此,如果您的CPU因服务硬件(IRQ)而中断,或者如果另一个CPU正在读取相同的内存,则不会影响结果,并且这些其他操作会将其视为已完成或未启动

例如。。。假设您想将某个变量设置为某个对象,但前提是之前未设置该变量。您可能倾向于这样做:

if (foo == 0)
{
   foo = some_function();
}
但如果这是并行运行的呢?这可能是程序将获取
foo
,将其视为零,同时线程2出现并执行相同的操作,并将值设置为某个值。回到原始线程,代码仍然认为
foo
为零,并且变量被赋值两次


对于这种情况,CPU提供一些指令,可以作为原子实体进行比较和条件赋值。因此,测试和设置、比较和交换以及加载链接/存储条件。您可以使用这些来实现锁(您的操作系统和C库已经实现了这一点),或者您可以编写依赖原语来完成某些操作的一次性算法。(这里有一些很酷的事情可以做,但大多数凡人都会避免这样做,因为他们害怕弄错。)

下面是我关于原子性的一些注释,可能会帮助你理解原子性的含义。这些注释来自最后列出的来源,如果你需要更透彻的解释,而不是像我这样的点式项目符号,我建议你阅读其中的一些。请指出任何错误,以便我改正

定义:

  • 源自希腊语,意思是“不能分成更小的部分”
  • “原子”操作总是被观察到是否完成,但是 从不半途而废
  • 原子操作必须完全执行,否则不执行 全部
  • 在多线程场景中,变量从未变异变为 直接变异,没有“中途变异”值
示例1:原子操作

  • 考虑不同线程使用的以下整数:

     int X = 2;
     int Y = 1;
     int Z = 0;
    
     Z = X;  //Thread 1
    
     X = Y;  //Thread 2
    
  • 在上面的示例中,两个线程使用X、Y和Z

  • 每次读写都是原子的
  • 线程将运行:
    • 如果线程1获胜,那么Z=2
    • 如果线程2获胜,那么Z=1
    • Z肯定是这两个值之一
示例2:非原子操作:++/-操作

  • 考虑递增/递减表达式:

    i++;  //increment
    i--;  //decrement
    
  • 这些操作转化为:

  • 读我
  • 增加/减少读取值
  • 将新值写回i
  • 每个操作都由3个原子操作组成,并且本身不是原子操作
  • 两次尝试在单独的线程上递增i
    i++;  //increment
    i--;  //decrement
    
      struct MyLong
       {
           public readonly int low;
           public readonly int high;
    
           public MyLong(int low, int high)
           {
               this.low = low;
               this.high = high;
           }
       }
    
    MyLong X = new MyLong(0xAAAA, 0xAAAA);   
    MyLong Y = new MyLong(0xBBBB, 0xBBBB);     
    MyLong Z = new MyLong(0xCCCC, 0xCCCC);
    
    X = Y; //Thread 1                                  
    Y = X; //Thread 2
    
    X.low = Y.low;      //Thread 1 - X = 0xAAAABBBB            
    Y.low = Z.low;      //Thread 2 - Y = 0xCCCCBBBB              
    Y.high = Z.high;    //Thread 2 - Y = 0xCCCCCCCC             
    X.high = Y.high;    //Thread 1 - X = 0xCCCCBBBB   <-- corrupt value for X
    
    using System.Threading;             
    
    int unsafeCount;                          
    int safeCount;                           
    
    unsafeCount++;                              
    Interlocked.Increment(ref safeCount);
    
       std::atomic< int> value;   
    
       void increment(){                                    
           ++value;                                
       }           
    
       void decrement(){                                         
           --value;                                                 
       }
    
       int get(){                                             
           return value.load();                                    
       }      
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class Counter
    {
         private AtomicInteger value= new AtomicInteger();
    
         public int increment(){
             return value.incrementAndGet();  
         }
    
         public int getValue(){
             return value.get();
         }
    }