Go:在Goroutine中执行无限循环代码

问题描述 投票:-1回答:1

我对goroutine感到困惑,使我感到困惑。

首先,在Go1.12环境中执行以下代码

func main() {
  var x int
  threads := runtime.GOMAXPROCS(0)
  for i := 0; i < threads; i++ {
    go func() {
      for {
        x++
      }
    }()
  }
  time.Sleep(time.Second)
  fmt.Println("x = ", x)
}

我无法获得结果,并且主要过程完全没有响应。

然后有人告诉我将环境更改为Go1.14,我做到了并得到结果:x = 0,但我期望的结果是x等于一个大于0的数字。

然后我尝试在goroutine中添加打印日志,如下所示:

func main() {
   // ... code ...
   go func() {
     for {
       fmt.Println("exec goroutine")
       x++
     }
   }()
   // ... code ...
}

我得到了我期望的结果,x = 61827(还有很多字符串exec goroutine)。

我的问题:

  • 关于上述问题,Go1.12和Go1.14有什么区别?

  • 为什么在goroutine中添加打印日志后为什么我得到正确的结果?

go goroutine
1个回答
-1
投票

为什么在goroutine中添加打印日志后为什么我得到正确的结果?

编辑:有关特定的go版本,请参阅@Volker的评论

操作

如果仅使用一个cpu进行操作(如无限循环),则它可能会保留调度程序(因此您的主要功能永远不会运行到结束)

print与可以释放调度程序的time.Sleep(time.Millisecond)相同

线程安全

Goroutine正在并行执行代码。第一个要理解的概念是线程安全性。 (即使goroutine不是线程,概念是相同的)

[运行代码时,您可以对同一变量x进行多重访问,但是由于操作x++或get操作不是原子的,因此不同的goroutine可能看不到相同的值变量。

一些资源可以帮助您了解:*How to make a variable thread-safe* https://en.wikipedia.org/wiki/Thread_safety

如何解决您的问题

有多种方法可以解决您的问题

选项1:使用原子操作

原子操作:

func main() {
  var x uint64
  threads := runtime.GOMAXPROCS(0)
  for i := 0; i < threads; i++ {
    go func() {
      for {
        atomic.AddUint64(&x, 1)
        time.Sleep(time.Millisecond)
      }
    }()
  }
  time.Sleep(time.Second)
  fmt.Println("x = ", x)
}

选项2:使用互斥锁

Mutex允许您同步对变量的访问,因此您将看到正确的值:

unc main() {
  var x int
  var mutex = &sync.Mutex{}
  for i := 0; i < threads; i++ {
    go func() {
      for {
        mutex.lock()
        x++
        mutex.Unlock()
        time.Sleep(time.Millisecond)
      }
    }()
  }
  time.Sleep(time.Second)
  mutex.lock()
  fmt.Println("x = ", x)
  mutex.Unlock()
}

其他选项

  • 使用读/写互斥锁
  • 使用通道传递值
© www.soinside.com 2019 - 2024. All rights reserved.