class atomicint is Int is repr('P6int'{ }

atomicint 是一个本机整数,大小为 CPU 提供的原子操作可以在其上执行。在 32 位 CPU 上,它通常为 32 位大小,在 64 位 CPU 上,它通常为 64 位大小。它的存在是为了允许编写使用原子操作的可移植代码。

注意:2017.08 之前的 Rakudo 版本不支持 atomicint

# Would typically only work on a 64-bit machine and VM build. 
my int64 $active = 0;
$active++;
 
# Would typically only work on a 32-bit machine and VM build. 
my int32 $active = 0;
$active++;
 
# Will work portably, though can only portably assume range of 32 bits. 
my atomicint $active = 0;
$active++;

使用 atomicint 类型不会自动提供原子性;它必须与原子操作结合使用。

# Correct (will always output 80000) 
my atomicint $total = 0;
await start { for ^20000 { $total++ } } xx 4;
say $total;
 
# *** WRONG *** due to lack of use of the atomicint type. 
# Either works correctly or dies, depending on platform. 
my int $total = 0;
await start { for ^20000 { $total++ } } xx 4;
say $total;
 
# *** WRONG *** due to lack of use of the atomic increment operator. 
my atomicint $total = 0;
await start { for ^20000 { $total++ } } xx 4;
say $total;

例程§

子例程 atomic-assign§

multi atomic-assign(atomicint $ is rwint $value)
multi atomic-assign(atomicint $ is rwInt() $value)

对本机整数执行原子赋值,该整数可能位于词法、属性或本机数组元素中。如果 $value 由于太大而无法解包为 64 位本机整数,则会引发异常。如果 atomicint 的大小只有 32 位,则超出范围的 $value 将被静默截断。atomic-assign 例程确保执行任何必需的屏障,以便将更改后的值“发布”到其他线程。

子例程 atomic-fetch§

multi atomic-fetch(atomicint $ is rw)

对本机整数执行原子读取,该整数可能存在于词法、属性或本机数组元素中。使用此例程而不是简单地使用变量可以确保看到来自其他线程对变量的最新更新,既可以通过执行任何必需的硬件屏障,又可以通过防止编译器提升读取。例如

my atomicint $i = 0;
start { atomic-assign($i1}
while atomic-fetch($i== 0 { }

肯定会终止,而在

my atomicint $i = 0;
start { atomic-assign($i1}
while $i == 0 { }

编译器可以合法地观察到 $i 在循环中没有更新,因此将读取提升出循环,从而导致程序永远不会终止。

子例程 atomic-fetch-inc§

multi atomic-fetch-inc(atomicint $ is rw)

对本机整数执行原子递增。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。返回在递增之前看到的该值。溢出将静默环绕。

子例程 atomic-fetch-dec§

multi atomic-fetch-dec(atomicint $ is rw)

对本机整数执行原子递减。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。返回在递减之前看到的该值。溢出将静默环绕。

子 atomic-fetch-add§

multi atomic-fetch-add(atomicint $ is rwint $value)
multi atomic-fetch-add(atomicint $ is rwInt() $value)

对本机整数执行原子加法。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。返回执行加法操作之前的所见值。溢出将默默环绕。如果 $value 太大而无法解包为 64 位整数,则会引发异常。如果 $value 溢出 atomicint,则会在执行加法操作之前将其默默截断。

子 atomic-fetch-sub§

multi atomic-fetch-sub(atomicint $ is rwint $value)
multi atomic-fetch-sub(atomicint $ is rwInt() $value)

对本机整数执行原子减法。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。返回执行减法操作之前的所见值。下溢将默默环绕。如果 $value 太大而无法解包为 64 位整数,则会引发异常。如果 $value 溢出 atomicint,则会在执行减法操作之前将其默默截断。

子 atomic-inc-fetch§

multi atomic-inc-fetch(atomicint $ is rw)

对本机整数执行原子增量。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。返回增量产生的值。溢出将默默环绕。

子 atomic-dec-fetch§

multi atomic-dec-fetch(atomicint $ is rw)

对本机整数执行原子减量。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。返回减量产生的值。溢出将默默环绕。

子 cas§

multi cas(atomicint $target is rwint $expectedint $value)
multi cas(atomicint $target is rwInt() $expectedInt() $value)
multi cas(atomicint $target is rw&operation)

对位置 $target 中的本机整数值执行原子比较并交换。前两种形式具有如下语义

my int $seen = $target;
if $seen == $expected {
    $target = $value;
}
return $seen;

但它作为一个单一的硬件支持的原子指令执行,就好像在执行期间阻止了对 $target 的所有内存访问一样。因此,可以从多个线程尝试该操作,而无需任何其他同步。例如

my atomicint $master = 0;
await start {
    if cas($master01== 0 {
        say "Master!"
    }
} xx 4

将可靠地仅打印 Master! 一次,因为只有一个线程会成功将 0 更改为 1。

$expected$value 都将强制转换为 Int,并在需要时解包。如果该值无法表示为 64 位整数,则会引发异常。如果 atomicint 的大小仅为 32 位,则这些值将默默截断为该大小。

第三种形式(采用代码对象)将首先对当前值进行原子获取,并使用它调用代码对象。然后,它将尝试对目标进行原子比较并交换,使用传递给代码对象的值作为 $expected,并使用代码对象的结果作为 $value。如果失败,它将读取最新值并重试,直到 CAS 操作成功。因此,可以将 atomicint $i 的原子乘法实现为 2

cas $i-> int $current { $current * 2 }

如果在计算 $current * 2 时另一个线程更改了该值,则该块将使用最新值再次被调用以进行进一步尝试,并且将重复此操作直到成功。

运算符§

中缀 ⚛=§

multi infix:<⚛=>(atomicint $ is rwint $value)
multi infix:<⚛=>(atomicint $ is rwInt() $value)

对本机整数执行原子赋值,该整数可能位于词法、属性或本机数组元素中。如果由于 $value 太大而无法解包为 64 位本机整数,则会引发异常。如果 atomicint 的大小仅为 32 位,那么超出范围的 $value 将被静默截断。⚛= 运算符确保执行任何必需的屏障,以便将更改后的值“发布”到其他线程。

前缀 ⚛§

multi prefix:<>(atomicint $ is rw)

对本机整数执行原子读取,该整数可能存在于词法、属性或本机数组元素中。使用此运算符而不是简单地使用变量可确保看到其他线程对变量的最新更新,既可以执行任何必需的硬件屏障,又可以防止编译器提升读取。例如

my atomicint $i = 0;
start { $i ⚛= 1 }
while ⚛$i == 0 { }

肯定会终止,而在

my atomicint $i = 0;
start { $i ⚛= 1 }
while $i == 0 { }

编译器可以合法地观察到 $i 在循环中没有更新,因此将读取提升出循环,从而导致程序永远不会终止。

前缀 ++⚛§

multi prefix:<++⚛>(atomicint $ is rw)

对本机整数执行原子增量。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。返回增量产生的值。溢出将默默环绕。

后缀 ⚛++§

multi postfix:<⚛++>(atomicint $ is rw)

对本机整数执行原子递增。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。返回在递增之前看到的该值。溢出将静默环绕。

前缀 --⚛§

multi prefix:<--⚛>(atomicint $ is rw)

对本机整数执行原子减量。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。返回减量产生的值。溢出将默默环绕。

后缀 ⚛--§

multi postfix:<⚛-->(atomicint $ is rw)

对本机整数执行原子递减。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。返回在递减之前看到的该值。溢出将静默环绕。

中缀 ⚛+=§

multi infix:<⚛+=>(atomicint $ is rwint $value)
multi infix:<⚛+=>(atomicint $ is rwInt() $value)

对本机整数执行原子加法。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。计算加法的结果。溢出将静默环绕。如果 $value 太大而无法解包为 64 位整数,则会引发异常。如果 $value 溢出 atomicint,则在执行加法之前,它将被静默截断。

中缀 ⚛-=§

multi infix:<⚛-=>(atomicint $ is rwint $value)
multi infix:<⚛-=>(atomicint $ is rwInt() $value)

对本机整数执行原子减法。这将使用硬件提供的原子操作来执行。由于该操作是原子的,因此无需获取锁即可安全使用。计算减法的结果。下溢将静默环绕。如果 $value 太大而无法解包为 64 位整数,则会引发异常。如果 $value 溢出 atomicint,则在执行减法之前,它将被静默截断。

中缀 ⚛−=§

使用 U+2212 减号的 ⚛-= 的同义词。