Golang中Goroutines不总是有效的问题探讨

Golang中Goroutines不总是有效的问题探讨 Go by Example: Goroutines

我一直在尝试通过这个学习Go语言,但在Playground上运行代码时得到的结果是:

直接:0 直接:1 直接:2 完成

Go协程类似于C语言中的fork吗?

我猜测是程序在Go函数执行完成前就结束了。

我添加了一个打印语句并改变了结果:

fmt.Println("test")

在–go f(“goroutine”)之前

直接:0 直接:1 直接:2 测试 执行中 协程:0 协程:1 协程:2 完成

可能需要说明示例页面上显示的结果可能不总是那样


更多关于Golang中Goroutines不总是有效的问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

感谢各位。我会继续努力的。之前我一直在通过gobyexample学习基础知识,之后会尝试写一些代码。虽然希望能慢慢学习,但可能还是会遇到一些困难。不过刚开始尝试的内容应该都不会太复杂。

更多关于Golang中Goroutines不总是有效的问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我并非刚开始学习并发编程,但也算不上专家。过去我处理过多线程程序并写过一些相关代码,但那已经是多年前的事了。这就是为什么我会问Go语言中的"go"是否类似于C语言中的"fork"。

另外我认为,既然这是一个学习网站,您可能需要说明实际结果可能与所述内容有所差异。

Go 语法确实更接近 C。

你可能需要解释结果可能与所述情况有所不同

在并发中,协程在不同的时间点启动,需要时间来初始化和执行某些操作。在此期间,程序会继续执行,稍后你才能看到协程的结果。并发中没有默认或保证的顺序,因此如果你按某种顺序启动几个协程,结果可能不会遵循该顺序。此外,并发并不等同于并行。

我同意George所说的。

我之前也有使用fork()系统调用进行多进程处理以及使用POSIX线程进行多线程编程的经验。这些经验帮助我理解了许多并发的核心概念。所以我认为你有一个很好的起点。

Go语言中的并发远不止这些,你需要学习Go语言是如何实现并发的,以及通道的用法——通道用于goroutine间的通信,包括数据传输和信号传递。

有很多优质的学习资源,包括各种Go语言书籍中的相关章节、专门讲解并发的书籍《Concurrency in Go》、视频课程中的教学内容(我认为@William Kennedy的《Ultimate Go》非常出色),还有YouTube上的视频。当你掌握了基础知识后,可以尝试搜索关于Go并发模式的视频。

func main() {
    fmt.Println("hello world")
}

并发是一个高级概念,不要从这一点开始学习Go。先尝试一些简单的例子来理解基本概念。关于您遇到的问题,并发有一些规则。类似于程序中的小进程,它由运行时调度,您经常会看到goroutine结束后执行下一条指令,然后goroutine才执行某些操作。这在并发中很常见,您会看到许多操作以意想不到的顺序执行。要让操作按顺序执行,您必须手动同步goroutine。

Medium Synchronized goroutines (part I) - golangspec - Medium

文章配图

同步的goroutine(第一部分)- golangspec - Medium

假设Go程序启动两个goroutine:

阅读时间:3分钟


推荐的入门书籍: http://www.golang-book.com/books/intro

在Go语言中,Goroutines的调度是非确定性的,这可能导致执行顺序与预期不符。你的观察是正确的:程序可能在Goroutines完成前就退出了。这通常是因为主Goroutine(main函数)结束时,所有其他Goroutines会被强制终止,无论它们是否执行完毕。

Goroutines与C语言中的fork有本质区别。C的fork创建的是独立的进程,拥有各自的内存空间;而Goroutines是轻量级线程,由Go运行时管理,共享内存空间,通过通道(channels)或同步原语(如sync.WaitGroup)进行通信和协调。

在Go by Example的代码中,缺少同步机制是导致输出不一致的原因。以下是一个修正版本,使用sync.WaitGroup确保所有Goroutines完成后再退出:

package main

import (
    "fmt"
    "sync"
)

func f(from string, wg *sync.WaitGroup) {
    defer wg.Done() // 通知WaitGroup此Goroutine完成
    for i := 0; i < 3; i++ {
        fmt.Println(from, ":", i)
    }
}

func main() {
    var wg sync.WaitGroup
    
    wg.Add(1) // 增加一个等待的Goroutine
    go f("goroutine", &wg)
    
    wg.Add(1)
    go f("direct", &wg)
    
    wg.Wait() // 等待所有Goroutines完成
    fmt.Println("done")
}

在这个示例中,sync.WaitGroup用于跟踪Goroutines的完成状态。wg.Add(1)在启动每个Goroutine前增加计数,defer wg.Done()在每个Goroutine结束时减少计数,wg.Wait()会阻塞直到计数为零。这确保了"done"在所有输出后打印。

输出顺序可能仍因调度而变,但所有Goroutines都会执行完毕。例如,可能输出:

direct : 0
direct : 1
direct : 2
goroutine : 0
goroutine : 1
goroutine : 2
done

goroutine : 0
direct : 0
goroutine : 1
direct : 1
goroutine : 2
direct : 2
done

在Go Playground上,由于环境限制,Goroutines的行为可能更不可预测,但添加同步机制是解决此类问题的标准方法。

回到顶部