作为Windows用户,我知道由于By default, Windows allocates 1 MB of memory for each thread’s user-mode stack.
,操作系统线程消耗大约1 Mb的内存golang
每个goroutine
如何使用〜8kb内存,如果操作系统线程很多更贪吃。是goroutine
种虚拟线程吗?
答案 0 :(得分:5)
Goroutines不是线程,它们是(来自spec):
...在同一地址空间内的独立并发控制线程或 goroutine 。
Effective Go将它们定义为:
它们被称为 goroutines ,因为现有的术语 - 线程,协同程序,进程等 - 传达了不准确的内涵。 goroutine有一个简单的模型:它是一个与同一地址空间中的其他goroutine同时执行的函数。它是轻量级的,比堆栈空间的分配花费更多。并且堆栈开始很小,所以它们很便宜,并且可以根据需要分配(和释放)堆存储来增长。
Goroutines没有自己的主题。相反,多个goroutine(可能)被多路复用到相同的OS线程上,因此如果应该阻塞(例如,等待I / O或阻塞信道操作),则其他goroutine继续运行。
可以使用runtime.GOMAXPROCS()
函数设置同时执行goroutines的实际线程数。引用runtime
包文档:
GOMAXPROCS变量限制了可以同时执行用户级Go代码的操作系统线程数。代表Go代码在系统调用中可以阻塞的线程数没有限制;那些不计入GOMAXPROCS限制。
请注意,在当前实现中,默认情况下只使用1个线程来执行goroutines。
答案 1 :(得分:2)
1 MiB是默认,正如您所正确指出的那样。您可以轻松选择自己的堆栈大小(但是,最小值仍然远高于~8 kiB)。
那就是说,goroutines不是线程。它们只是具有协同多任务的任务,类似于Python。 goroutine本身就是做你想做的事情所需的代码和数据;还有一个单独的调度程序(在更多OS线程上运行),实际执行该代码。
在伪代码中:
loop forever
take job from queue
execute job
end loop
当然,execute job
部分可能非常简单,也可能非常复杂。你能做的最简单的事情就是执行一个给定的委托(如果你的语言支持这样的话)。实际上,这只是一个方法调用。例如,在更复杂的场景中,还可能存在诸如恢复某种上下文,处理延续和协同任务产生等内容。
这是一种非常轻量级的方法,在进行异步编程时非常有用(现在几乎所有东西都是:))。许多语言现在支持类似的东西 - 很久以前,Python是我见过的第一个(“tasklets”)。当然,在没有先发制人的多线程的环境中,这几乎是默认的。
例如,在C#中,有Task
个。它们与goroutine并不完全相同,但在实践中,它们非常接近 - 主要区别在于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
s更容易避免,但仅仅因为你拥有的工具,而不是Task
所做的任何“魔法”。
与goroutines在这方面的主要区别在于Go并没有真正意义上的阻塞方法。它们不是阻塞,而是将它们的特定异步请求排队,并产生。当操作系统(以及Go中的任何其他层 - 我对内部工作没有深入了解)收到响应时,它会将其发布到goroutine调度程序,后者又知道“等待”响应的goroutine是现在准备恢复执行;当它实际上获得一个插槽时,它将从“阻塞”调用继续进行,就好像它确实是阻塞一样 - 但实际上,它与C#的await
非常相似。没有基本的区别 - C#的方法与Go的差异很大,但它们并不是那些巨大的。
并且还要注意,这基本上与旧Windows系统上使用的方法相同,没有先发制人的多任务处理 - 任何“阻塞”方法都会简单地将线程的执行返回给调度程序。当然,在这些系统上,你只有一个CPU内核,所以你不能一次执行多个线程,但原理仍然是相同的。
答案 2 :(得分:0)
goroutines就是我们所说的green threads。它们不是OS线程,go调度程序负责它们。这就是为什么它们可以有更小的内存占用量。