当某种东西被认为是原子性的或者具有原子性的时候,这意味着在它运行的环境中,它是不可分割的或不可中断的。

那么这到底意味着什么,为什么在使用并发代码时知道这很重要?

第一件非常重要的事情就是了解“上下文(context)”这个词。在某个特定的上下文中,有的操作可能是原子的,有的可能不是。 在你的流程环境中,原子状态的操作在操作系统环境中可能不是原子操作; 在操作系统环境中是原子的操作在你的机器环境中可能不是原子的; 并且在机器上下文中是原子的操作在应用程序上下文中可能不是原子的。 换句话说,操作的原子性可以根据当前定义的范围而改变。 清醒的认识到这个事实对你程序的利弊是非常重要的!

在考虑原子性时,经常需要做的第一件事是定义上下文或作用域,这个操作将被视为原子性的。这是思考程序的基础。

2006年,游戏公司Blizzard成功起诉了MDY Industries 600万美元源于其开发的名为“滑翔机”的程序,该程序可在没有用户干预的情况下自动操作他们的游戏“魔兽世界”。 这些类型的程序通常被称为“机器人外挂”。
当时,魔兽世界有一个反作弊程序叫做“守望者“,它可以在你玩游戏的任何时候运行。除此之外,守望者将扫描主机的内存并运行启发式查找似乎用于作弊的程序。
利用原子上下文的概念,滑翔机成功避免了守望者的这种检查。 守望者认为扫描机器上的内存是一种原子操作,但在开始扫描之前,滑翔机利用硬件中断来隐藏自己!守望者守护进程的内存扫描在进程的上下文中是原子的,但不在操作系统的上下文中。

现在让我们看一下术语“不可分割”和“不可中断”。这些术语意味着在你定义的上下文中,原子的东西将在整个过程中发生,而不会同时发生任何事情。 这让人有点糊涂,所以我们来看一个例子:

i++

这是一个任何人都可以明白的简单代码,但它很容易证明原子性的概念。 它可能看起来很原子,但是一个简单的分析揭示了几种操作:

  • 检索i的值。
  • 增加i的价值。
  • 存储i的值。

尽管这些操作中的每一个都是原子的,但三者的组合可能不是,取决于你的上下文。 这揭示了原子操作的一个有趣特性:将它们结合并不一定会产生更大的原子操作。 创建操作原子取决于你希望它在哪个上下文中处于原子状态。 如果你的上下文是一个没有并发进程的程序,那么这个代码在该上下文中是原子的。 如果你的上下文是一个不会将 i 暴露给其他goroutine的goroutine,那么这个代码是原子的。

为什么我们如此在意原子性?原子性非常重要,因为如果说某些东西是原子的,那么就隐式地意味着在并发环境中是安全的。这使我们能够编写逻辑上正确的程序,并且——我们稍后将看到——甚至可以用作优化并发程序的一种方式。

大多数语句不是原子的,更不用说函数,方法和程序了。如果原子性是组成逻辑上正确的程序关键,并且大多数语句不是原子的,那么我们如何调和这两个问题?稍后我们会深入探讨,但总之,我们可以通过采用各种技术来强制原子性。至于如何决定你的代码的哪些部分需要是原子的,以及需要划分到什么样的粒度,我们在下一节继续讨论。

最后编辑: kuteng  文档更新时间: 2021-01-02 17:30   作者:kuteng