goroutines最大数量

我可以使用多less个肠衣? 例如维基百科说,在Erlang中,可以创build2000万个进程而不会降低性能。

更新:我刚刚在goroutines性能上调查了一下,得到了这样的结果:

  • 看起来goroutine的寿命更多的是计算sqrt()1000次(〜45μs),唯一的限制就是内存
  • Goroutine花费4 – 4.5 KB

如果门厅被封锁,除了以下情况外,不涉及任何费用:

  • 内存使用情况
  • 较慢的垃圾收集

成本(根据内存和实际开始执行goroutine的平均时间)是:

Go 1.6.2 (April 2016) 32-bit x86 CPU (A10-7850K 4GHz) | Number of goroutines: 100000 | Per goroutine: | Memory: 4536.84 bytes | Time: 1.634248 µs 64-bit x86 CPU (A10-7850K 4GHz) | Number of goroutines: 100000 | Per goroutine: | Memory: 4707.92 bytes | Time: 1.842097 µs Go release.r60.3 (December 2011) 32-bit x86 CPU (1.6 GHz) | Number of goroutines: 100000 | Per goroutine: | Memory: 4243.45 bytes | Time: 5.815950 µs 

在安装了4 GB内存的机器上,这将最大的goroutine数量限制在略低于100万的范围内。


源代码(如果您已经理解了上面打印的数字,则无需阅读):

 package main import ( "flag" "fmt" "os" "runtime" "time" ) var n = flag.Int("n", 1e5, "Number of goroutines to create") var ch = make(chan byte) var counter = 0 func f() { counter++ <-ch // Block this goroutine } func main() { flag.Parse() if *n <= 0 { fmt.Fprintf(os.Stderr, "invalid number of goroutines") os.Exit(1) } // Limit the number of spare OS threads to just 1 runtime.GOMAXPROCS(1) // Make a copy of MemStats var m0 runtime.MemStats runtime.ReadMemStats(&m0) t0 := time.Now().UnixNano() for i := 0; i < *n; i++ { go f() } runtime.Gosched() t1 := time.Now().UnixNano() runtime.GC() // Make a copy of MemStats var m1 runtime.MemStats runtime.ReadMemStats(&m1) if counter != *n { fmt.Fprintf(os.Stderr, "failed to begin execution of all goroutines") os.Exit(1) } fmt.Printf("Number of goroutines: %d\n", *n) fmt.Printf("Per goroutine:\n") fmt.Printf(" Memory: %.2f bytes\n", float64(m1.Sys-m0.Sys)/float64(*n)) fmt.Printf(" Time: %f µs\n", float64(t1-t0)/float64(*n)/1e3) } 

成千上万,每Go常见问题: 为什么goroutines而不是线程? :

在同一个地址空间中创build数十万个goroutines是实用的。

testingtesting/ chan / goroutines.go创造了10,000个,并且可以轻松地做更多的事情,但是被devise为快速运行; 您可以更改系统上的号码进行试验。 你可以很容易地运行数百万,给予足够的内存,如在服务器上。

要了解最大数量的goroutines,请注意,根据goroutine成本主要是堆栈。 每次常见问题:

… goroutines,可以很便宜:它们的堆栈内存很less,只有几千字节。

一个后台计算是假定每个goroutine有一个4 KiB 页面分配给堆栈(4 KiB是一个非常统一的大小),加上一些控制块(如线程控制块 )的一些小的开销运行时间; 这与你观察到的一致(2011年,前1.0)。 因此,100K的例程大约需要400MB的内存,而1M的例程需要大约4GB的内存,这在桌面上仍然是可pipe理的,对于手机来说还是可以pipe理的,并且在服务器上是非常易于pipe理的。 实际上,起始栈的大小范围从半页(2 KiB)到两页(8 KiB),所以这大致是正确的。

起始堆栈大小随着时间而改变; 它从4 KiB(1页)开始,然后在1.2增加到8 KiB(2页),然后在1.4下降到2 KiB(半页)。 这些变化是由于分段堆栈在分段之间快速切换时导致性能问题(“热堆分离”),因此增加到减less(1.2),然后在分段堆栈被连续堆栈(1.4)replace时降低:

Go 1.2发行说明: 堆栈大小 :

在Go 1.2中,创buildgoroutine时的最小堆栈大小已经从4KB提升到了8KB

Go 1.4发行说明: 对运行时间的更改 :

goroutine的堆栈在1.4中默认的起始大小已经从8192字节减less到2048字节。

每个常规内存很大程度上是堆栈式的,并且从低开始增长,所以你可以很便宜地有很多的例程。 你可以使用一个较小的起始堆栈,但是它将不得不更快地增长(以时间成本获得空间),并且由于控制块不缩小,益处减less。 至less在换出时(例如,在堆上进行所有分配,或在上下文切换时将堆栈保存到堆上),可以消除堆栈,尽pipe这会损害性能并增加复杂性。 这是可能的(就像在Erlang中一样),意味着你只需要控制块和保存的上下文,允许在goroutine数量上有5×-10×的另一个因子,现在受限于控制块大小和goroutine的堆上大小本地variables。 然而,这不是非常有用,除非你需要数以百万计的小睡房。

由于有很多协程的主要用途是IO界限的任务(特别是处理阻塞系统调用,尤其是networking或文件系统IO),所以更有可能遇到其他资源上的操作系统限制,即networking套接字或文件句柄: golang-nuts> goroutines和文件描述符的最大数量? 。 解决这个问题的通常方法是使用稀缺资源池 ,或者更简单地通过信号量限制数量; 请参阅在Go中保存文件描述符并在Go中 限制并发 。

这完全取决于你正在运行的系统。 但是goroutines是非常轻量级的。 一个平均的过程应该不会有100.000个并发例程的问题。 当然,如果不知道这个平台是什么的话,那么这个平台是否适合你的目标平台呢?

换句话说,有谎言,该死的谎言和基准。 正如Erlang基准的作者所承认的那样,

不言而喻,机器中没有足够的内存来实际做任何有用的事情。 压力testingerlang

什么是您的硬件,什么是您的操作系统,您的基准源代码在哪里? 什么是基准testing和certificate/反驳?

以下是Dave Cheney撰写的一篇关于这个主题的文章: http : //dave.cheney.net/2013/06/02/why-is-a-goroutines-stack-infinite

如果goroutine的数量成为问题,你可以很容易地限制你的程序:
看到mr51m0n / gorc和这个例子 。

在运行的程序的数量上设置阈值

可以在启动或停止一个goroutine时增加和减less一个计数器。
它可以等待运行的最小或最大goroutines数量,从而允许同时运行gorc治理的goroutines的数量的阈值。