• 云学
    2018-10-09
    请问这个例子中go routine对变量i的捕获是引用?
     2
     10
  • yandongxiao
    2018-09-27
    创建能创建成千上万个goroutine,但是不一定有那么多的系统资源,比如一般程序的最大可以打开4096个文件描述符。所以需要对goroutine 的启用数量加以限制。常用方法:
    1. buffered channel
    2. WaitGroup
     2
     7
  • cygnus
    2018-09-17
    除了用带缓冲的通道,还可以用runtime.GOMAXPROCS(maxProcs)来控制Goroutine并发数

    作者回复: 这是控制P的数量的。

    
     7
  • 哈噜噜
    2019-03-01
    请问,该示例代码中,当go函数是一个闭包?而传给fmt.Println(a ...interface{})的是变量i的引用吗?

    作者回复: Go语言里只有传值,没有传引用。如果go函数是无参数的匿名函数,那么在它里面的fmt.Println函数的参数只会在go函数被执行的时候才会求值。到那个时候,i的值可能已经是10(最后一个数)了,因为for语句那时候可能已经都执行完毕了。

    
     6
  • 孙稚昊
    2019-04-17
    如果是
    package main

    import "fmt"

    func main() {
        for i := 0; i < 10; i++ {
            go func(i int) {
                fmt.Println(i)
            }(i)
        }
    }
    这样子的话,i输入就是0-9了吧
    展开

    作者回复: 我再强调一下。在go语句执行后,Go运行时系统会把对应的go函数装进新启用的goroutine中,随后调度执行。因为这个调度是不保证先后顺序的,所以这些go函数的执行在默认情况下也是乱序的。因此,你这样写无法保证数字的顺序打印。

     1
     4
  • 🚲🏃🏊
    2018-10-04
    限制G的数量,可以使用goroutine pool
    
     3
  • 坤坤
    2019-10-03
    既然 GPM 分层模型定义了 G 与 M 可以多对多,并且 G 的创建代价很小数量没有限制。为什么要对 goroutine 的启用数量加以限制?

    作者回复: 主要是因为计算机的内存(以及其他资源)总是有限的。从程序设计的角度讲,限制某种执行高并发任务的 goroutine 的数量也是很有必要的。另外,单进程内数十万的 goroutine 也会对 Go 语言的调度器和操作系统带来不小的压力。

    再有,我们应该尽量地去量化程序对资源的使用,并且有节制地区使用资源。当然,具体的使用上限设定成多少合适,还有以实际压测的结果为准。

    
     2
  • ArtistLu
    2019-09-15
    老师请问下文中提到,这类队列中的 G 总是会按照先入先出的顺序……和Go 语言并不会去保证这些 goroutine 会以怎样的顺执行如何理解勒?

    作者回复: 可运行 G 队列里面是先入先出的,可是调度器里有多个可运行 G 队列(每个 P 都有一个),而且哪个 G 什么进入哪个可运行G 队列还另有规则。所以这两句话并不矛盾。

    
     2
  • colben
    2018-09-21
    "那也肯定是运气好而已"
    “总结”前的最后这句话说得太对了!
    
     2
  • Yayu
    2018-09-17
    不认为 buffered channel 的容量可以限制 goroutine 的数量,这个数量的最佳值应该跟硬件配置相关,但是如何限制应该是一个runtime 的参数来限制。具体是什么,还真不清楚。
    
     2
  • oyt
    2018-09-17
    要限制goroutine的启用数量,即达到规定限制数量后就阻塞。 可以使用有缓冲的channel,例如chanCount:=make(chan int,10) 可以限制启用10个goroutine。
    
     2
  • 开开心心
    2018-09-17
    这样子为什么会造成阻塞?我就加了    time.Sleep(1*time.Microsecond),而且时间很短
    for i := uint32(0); i < 10; i++ {
            go func(i uint32) {
                fn := func() {
                    time.Sleep(1*time.Microsecond)
                    fmt.Println(i)
                }
                //fmt.Println("准备",i)
                trigger(i, fn)
            }(i)
        }
    展开

    作者回复: 不应该,我这里也正常,不知道你还改了什么。

    
     1
  • 木杉
    2020-01-20
    加锁
    
    
  • 水先生
    2020-01-03
    func main() {
        for i := 0; i < 10; i++ {
            fmt.Println(i) // the first Print
            go func() {
                fmt.Println(i)    // the second Print
            }()
        }
    }
    -------
    第一个print算是为go函数争取了执行时间吗?得到的结果是0至9顺序,然后3,6,10,10,10。
    按理,循环到10,go函数不是应该结束了么?为啥还会多两个10的呀?
    麻烦老师~
    展开

    作者回复: 这里的两个print是做对比用的吧。0~9顺序打印应该是第一个print打印出来的。你可以把两个print打印的东西分别加上不同的前缀。这样就容易区分了。

    go函数的执行会稍微滞后一些,所以当 for 语句执行完的时候(迭代变量 i 会定格在 10 这个值上),有的go函数可能还没开始执行。等到它们执行的时候,打印变量 i,就只会打印出 10。

    
    
  • 疯琴
    2019-12-28
    第二遍读,理解得更加深刻了。字字句句将原理解释得清晰到位。
    
    
  • 李小锋
    2019-12-24
    goroutine 代表着并发模型中的用户级线程。
    一个进程就是某个程序运行时的产物。
    线程总是在进程之内,它可以被视为进程中运行着的控制流。
    每个进程的第一个线程都会随着该进程的启动而创建,他们可以被称为所属进程的主线程。
    主线程之外的其他线程都由代码显示的创建和销毁。
    Go语言运行时(runtime)系统会帮助我们自动创建和销毁系统级线程。
    Go 语言不但有独特的并发编程模型,以及用户级线程goroutine ,还拥有强大的goroutine 。
    调度器是Go 语言运行时(runtime)的重要组成部分,它主要负责统筹调配go 并发编程模型中的三个主要元素:
    Goroutine
    processor,
    mechine,系统级线程
    G和M 之间,由于P的存在呈现出,多对多的关系
    当一个G由于某些原因暂停时,P 会及时发现,并把相应的M 分开,给其他的G 使用。
    展开
    
    
  • x
    2019-11-14
    sync.WaitGroup来限制goroutine的数量? 每启动一个ADD一次,执行完再done一次,为了防止主线程的结束可以在循环后加上wait。

    作者回复: sync.WaitGroup 用在这里不是很好。

    
    
  • prader
    2019-11-02
    因为goroutine是异步并发执行,所以有可能什么打印不出来,也有可能,打印出的是乱序的0-9
    
    
  • 党
    2019-07-26
    这个题的例子肯定是没有任何显示,因为主线程都结束了,依托这个主线程的一切都灰飞烟灭了,但是要是后边加个定时,那会无序的显示0到9,因为每次for循环会把i的值拷贝到打印参数里。是一个副本不是原来的i
     1
    
  • jacke
    2019-06-19
    问下:
    func main() {
        fmt.Println("cup ",runtime.NumCPU())
        for i := 0; i < 10; i++ {
            go func() {
                fmt.Println(i, &i)
            }()
        }
        time.Sleep(time.Second)
    }
    结果:
    cup 4
    10 0xc420016088
    10 0xc420016088
    10 0xc420016088
    10 0xc420016088
    10 0xc420016088
    10 0xc420016088
    10 0xc420016088
    4 0xc420016088
    10 0xc420016088
    10 0xc420016088
     
    疑问:go routine的调度是随机,按照郝老师的讲解,在go routine得到执行的时候 fmt.Println,前面的i以及是10了,为什么后面还有打印是4的情况,而且看出来i地址一样,应该是同一个值?是不是go routine执行是并行的原因,所以打印到屏幕显示缓冲区,最后是乱序?
    展开

    作者回复: 可以从两个方面理解:

    1. 这些 go 函数的执行顺序是不固定的。这个想必你已经理解了。
    2. 当一个 fmt.Println 引发 I/O 操作的时候也可能被中断(被切换下 CPU)。当它再次获得运行机会的时候,也许其他的 fmt.Println 都打印完了。这种切换其实就是 Go 运行时系统进行调度的结果。

     1
    
我们在线,来聊聊吧