9.1 竞态
在串行程序中(即一个程序只有一个goroutine),程序中各个步骤的执行顺序由程序逻辑来决定。比如,在一系列语句中,第一句在第二句之前执行,以此类推。当一个程序有两个或者多个goroutine时,每个goroutine内部的各个步骤也是顺序执行的,但我们无法知道一个goroutine中的事件x和另外一个goroutine中的事件y的先后顺序。如果我们无法自信地说一个事件肯定先于另外一个事件,那么这两个事件就是并发的。
考虑一个能在串行程序中正确工作的函数。如果这个函数在并发调用时仍然能正确工作,那么这个函数是并发安全(concurrency-safe)的,在这里并发调用是指,在没有额外同步机制的情况下,从两个或者多个goroutine同时调用这个函数。这个概念也可以推广到其他函数,比如方法或者作用于特定类型的一些操作。如果一个类型的所有可访问方法和操作都是并发安全时,则它可称为并发安全的类型。
让一个程序并发安全并不需要其中的每一个具体类型都是并发安全的。实际上,并发安全的类型其实是特例而不是普遍存在的,所以仅在文档指出类型是安全的情况下,才可以并发地访问一个变量。对于绝大部分变量,如要回避并发访问,要么限制变量只存在于一个goroutine内,要么维护一个更高层的互斥不变量。本章将详细解释这些概念。
与之对应的是,导出的包级别函数通常可以认为是并发安全的。因为包级别的变量无法限制在一个goroutine内,所以那些修改这些变量的函数就必须采用互斥机制。
函数并发调用时不工作的原因有很多,包括死锁、活锁(livelock)[1]以及资源耗尽。我们没有足够的时间来讨论所有的情形,因此接下来会重点讨论最重要的一种情形,即竞态。
竞态是指在多个goroutine按某些交错顺序执行时程序无法给出正确的结果。竞态对于程序是致命的,因为它们可能会潜伏在程序中,出现频率也很低,有可能仅在高负载环境或者在使用特定的编译器、平台和架构时才出现。这些都让竞态很难再现和分析。 ...
Get Go程序设计语言 now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.