Goroutines 8kb和Windows操作系统线程1 mb

作为Windows用户,我知道操作系统线程消耗约1 Mb的内存,由于By default, Windows allocates 1 MB of memory for each thread's user-mode stack. 如果操作系统线程更贪吃, golang如何为每个goroutine使用golang的内存。 goroutine什么样的虚拟线程?

Goroutines不是线程,他们是(从规格 ):

在同一地址空间内的一个独立的并发控制线程或goroutine

Effective Go将它们定义为:

它们被称为goroutines,因为现有的术语(线程,协程,进程等等)传递的是不准确的内涵。 goroutine有一个简单的模型:它是一个在同一个地址空间中与其他goroutines同时执行的函数。 它是轻量级的,比堆栈空间的分配花费更多。 堆栈起点很小,所以它们很便宜,而且根据需要分配(并释放)堆存储空间以增加堆栈。

Goroutines没有自己的线程。 相反,多个goroutines被多路复用到相同的OS线程上,所以如果一个应该阻塞(例如等待I / O或者阻塞通道操作),其他的继续运行。

可以使用runtime.GOMAXPROCS()函数来设置同时执行goroutine的实际线程数。 从runtime包文档引用:

GOMAXPROCS变量限制了可以同时执行用户级Go代码的操作系统线程的数量。 系统调用中代表Go代码的线程数量没有限制; 那些不符合GOMAXPROCS限制。

请注意,在当前实现默认情况下,只有一个线程用于执行goroutines。

1 MiB是默认值 ,正如您正确指出的那样。 你可以很容易地选择自己的堆栈大小(但是,最小值仍然比〜8kB大得多)。

也就是说,goroutines不是线程。 他们只是具有共同任务的任务,类似于Python的。 goroutine本身就是做你想要的代码和数据。 还有一个单独的调度程序(在多个操作系统线程上运行),它们实际执行该代码。

在伪代码中:

 loop forever take job from queue execute job end loop 

当然, execute job部分可以非常简单,也可以非常复杂。 你可以做的最简单的事情就是执行一个给定的委托(如果你的语言支持这样的话)。 实际上,这只是一个方法调用。 在更复杂的情况下,例如,还可以存在诸如恢复某种上下文,处理延续和协作任务收益的东西。

这是一个非常轻量级的方法,在进行异步编程时非常有用(现在几乎所有东西都是:))。 很多语言现在都支持类似的东西 – Python是我在第一个看到这个(“tasklets”)之前的东西。 当然,在没有先发制人的多线程环境下,这几乎是默认的。

在C#中,例如,有Task 。 它们并不完全相同,但在实践中,它们非常接近 – 主要区别在于Task使用线程池中的线程(通常是),而不是单独的专用“调度程序”线程。 这意味着如果你启动了1000个任务,它们有可能被1000个独立的线程运行; 在实践中,它会要求你编写非常糟糕的Task代码(例如,只使用阻塞I / O,睡眠线程,等待等待句柄等)。 如果你使用Task来实现异步非阻塞I / O和CPU的工作,那么在实际操作中它们会非常接近goroutines。 理论有点不同:)

编辑:

为了澄清一些混淆,下面是典型的C#异步方法的样子:

 async Task<string> GetData() { var html = await HttpClient.GetAsync("http://www.google.com"); var parsedStructure = Parse(html); var dbData = await DataLayer.GetSomeStuffAsync(parsedStructure.ElementId); return dbData.First().Description; } 

GetData方法的角度来看,整个处理过程是同步的 – 就好像您根本没有使用异步方法一样。 关键的区别是,当你在做“等待”的时候,你并没有使用线程。 但忽略这一点,它几乎和编写同步分块代码一样。 这也适用于共享状态的任何问题,当然,多线程问题在await和阻塞多线程I / O方面没有太大区别。 使用Task可以避免更容易,但仅仅是因为你拥有的工具,而不是因为Task的“魔法”。

goroutines在这方面的主要区别在于Go并没有真正的通常意义上的阻塞方法。 而不是阻塞,他们排队他们特定的异步请求,并屈服。 当操作系统(以及Go中的任何其他层 – 我对内部工作没有深入的了解)收到响应后,会将其发送到goroutine调度程序,goroutine调度程序又知道“等待”响应的goroutine是现在准备恢复执行; 当它实际上得到一个插槽时,它将继续从“阻塞”调用,如果它真的阻塞 – 但实际上,它是非常类似于C#正在await 。 没有什么根本的区别,C#的方法和Go的方法有很多不同,但并不是那么

还要注意,这与旧版Windows系统使用的方法基本相同,没有先发制人的多任务处理 – 任何“阻塞”方法都只是让线程的执行回到调度器。 当然,在这些系统上,你只有一个CPU核心,所以你不能一次执行多个线程,但是原理还是一样的。

goroutines是我们所说的绿色线程 。 他们不是操作系统线程,去调度程序负责他们。 这就是为什么他们可以有更小的内存占用。