【问题标题】:Is synchronization required?是否需要同步?
【发布时间】:2015-08-12 10:51:18
【问题描述】:

我在一个对象中定义了一个变量(r.something)

func (r *Runner) init() {
  r.something = make(map[string]int)
  r.something["a"]=1

  go r.goroutine()
}

而 r.goroutine 使用存储在 r.something 中的值而没有同步。 除了 r.goroutine() 没有其他人会读/写这个值

不同步是否安全?

换句话说:我想重用一个在其他地方初始化的goroutine中的一些变量在 goroutine开始之前。那安全吗?

补充问题: 在 r.goroutine() 完成后,我希望能够从其他地方使用 r.something(没有与其他 goroutine 的读/写重叠)。也安全吗?

【问题讨论】:

    标签: go goroutine


    【解决方案1】:

    当然这是安全的,否则用 Go 编程可能是一场噩梦(或者至少不那么令人愉快)。 The Go Memory Model 是一篇有趣的文章。

    例程创建是一个同步点。有一个和你非常相似的例子:

    var a string
    
    func f() {
        print(a)
    }
    
    func hello() {
        a = "hello, world"
        go f()
    }
    

    附上以下评论:

    调用 hello 将在未来某个时间点打印“hello, world”(可能在 hello 返回之后)。

    这是因为:

    启动新 goroutine 的 go 语句发生在 goroutine 执行开始之前。

    before 这个词在这里很重要,因为它意味着例程创建(在一个线程中)必须与其开始(可能在另一个线程中)同步,因此写入 a必须对新例程可见。

    【讨论】:

    • 完全有道理!谢谢@tomasz
    【解决方案2】:

    如果不存在不同go-routines的read和write操作重叠可能发生在这个变量上的情况,那么你是对的:不需要用于任何同步。

    正如你所提到的,变量是在在你的 go-routine 开始之前初始化的,你实际上是安全的。

    【讨论】:

    • 在一个操作系统线程中修改的变量可能对另一个操作系统线程不可见,除非访问被包装到像sync.Mutex这样的显式同步中。我知道我们在谈论 goroutines,但在底层它归结为操作系统线程,尽管并非总是如此。与goroutine启动前的变量初始化有什么关系,golang是否在goroutine启动前对内部某处捕获的变量进行隐式同步?
    • 我们需要在这里准确地理解术语“可见”:当然,对于给定进程的每个线程,变量都是可见的(如:完全可见)。问题是:线程 A 所做的更改是否对线程 B 准时可见。如果两个线程同时访问变量,情况可能并非如此。但是由于第一个线程(我将其用作 go-routines 的同义词:两者的行为非常相似)早已在第二个线程存在之前完成了它的修改,所以我们是安全的。 go运行时没有隐式同步,隐式同步由您的代码完成。
    【解决方案3】:

    是的,它是安全的。根据Go Memory Model:

    • 启动新 goroutine 的 go 语句发生在 goroutine 开始执行之前
    • 在单个 goroutine 中,happens-before 顺序是程序表达的顺序

    这意味着您在启动 goroutine 之前对变量所做的所有更改都在此 goroutine 中可见。

    回答您的附加问题:视情况而定。一般来说,如果r.goroutine()修改了r.something,并且你想从另一个goroutine读取它,你需要使用同步。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-17
      • 2013-07-04
      • 2015-02-04
      相关资源
      最近更新 更多