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

时间:2015-05-06 10:00:32

标签: windows multithreading go threadpool

作为Windows用户,我知道由于By default, Windows allocates 1 MB of memory for each thread’s user-mode stack.,操作系统线程消耗大约1 Mb的内存golang每个goroutine如何使用〜8kb内存,如果操作系统线程很多更贪吃。是goroutine种虚拟线程吗?

3 个答案:

答案 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调度程序负责它们。这就是为什么它们可以有更小的内存占用量。