Golang对象作用域与循环和线程

It's clear I don't understand something about scoping. I've distilled the problem down to the following (run it at The Go Playground):

package main

import (
    "log"
    "time"
)

type mystruct struct {
    mybool bool
}

func new() mystruct {
    var ms mystruct
    go func() {
            for {
                    time.Sleep(time.Second/2)
                    log.Println("loop says:",ms.mybool)
            }
    }()
    return ms
}

func (m *mystruct) switchIt() {
    if m.mybool {
            m.mybool = false
    } else {
            m.mybool = true
    }
}

func main() {
    thing := new()

    time.Sleep(2 * time.Second)
    thing.switchIt()

    time.Sleep(2 * time.Second)
    thing.switchIt()

    time.Sleep(2 * time.Second)
    thing.switchIt()
}

As it is now, the boolean is always false from the perspective of the endless loop in new()'s anonymous function. I'm trying to change that value with switchIt() while the loop runs.

Moving the boolean to a global variable gives the behavior I want (value changed from the loop's perspective), but I need the boolean to be part of the object because there will be several instantiations of the object.

I modified your code a bit, and now it works as your expectation.


Code

package main

import (
    "log"
    "time"
)

type mystruct struct {
    mybool bool
}

func newThing() *mystruct {
    var ms mystruct
    go func() {
        for {
            time.Sleep(time.Second / 2)
            log.Println("loop says:", ms.mybool)
        }
    }()
    return &ms
}

func (m *mystruct) switchIt() {
    if m.mybool {
        m.mybool = false
    } else {
        m.mybool = true
    }
}

func main() {
    thing := newThing()

    time.Sleep(2 * time.Second)
    thing.switchIt()

    time.Sleep(2 * time.Second)
    thing.switchIt()

    time.Sleep(2 * time.Second)
    thing.switchIt()
}

Tips

Issues of your code:

  • I changed your new() to newThing(), because it's confusing with the built-in new() function.
    Even though you can write it that way, but it's error-prone.
  • Your original new() returns a copy of the struct, thus after calling new(), there are 2 copies of the struct, that's why you can never change it.
    In this case it's better to return a pointer instead.

Tips on go:

  • For struct, when assign / pass as param / return from function, it always makes a copy.
    You have 2 options to conquer this:
    • Use a pointer instead.
      In that case there is only one copy of the struct.
    • Let fields in the struct be pointers or similar reference types (e.g slice or map).
      In that case, there are 2 copies of the struct, but those fields refer to the same underling data structure.
      But in you code, bool is not a reference type, so can't use this.
  • Similar to struct, array behavior similarly.
    Though most of the time, you will only use slice instead.
  • On the other, slice / map contain reference to underling data structure that hold the actual data.
    So, if you assign a slice to another slice variable, or return it from a function, there will be 2 slice, but they still refer to the same underling data structure (an array).
    Thus, changes made to one slice would be visible to the other.

BTW, Effective go is a good place to pick up these tricky details about go :)