Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell IO操作是否在绿色线程中运行?_Haskell - Fatal编程技术网

Haskell IO操作是否在绿色线程中运行?

Haskell IO操作是否在绿色线程中运行?,haskell,Haskell,举一个例子: do a1来自 此模块提供一组操作,用于异步运行IO操作并等待其结果。它是Control.Concurrent提供的基本并发操作之上的一个薄层 及 Haskell线程的调度在Haskell运行时系统内部完成,不使用任何操作系统提供的线程包 如果不仔细解释,最后一个可能有点误导:虽然Haskell线程的调度——即选择下一步运行哪个Haskell代码——是在不使用任何操作系统设施的情况下完成的,但GHC可以并且确实使用多个操作系统线程来实际执行选择要运行的任何代码,至少在使用线程运行时

举一个例子:


do a1来自

此模块提供一组操作,用于异步运行IO操作并等待其结果。它是Control.Concurrent提供的基本并发操作之上的一个薄层

Haskell线程的调度在Haskell运行时系统内部完成,不使用任何操作系统提供的线程包


如果不仔细解释,最后一个可能有点误导:虽然Haskell线程的调度——即选择下一步运行哪个Haskell代码——是在不使用任何操作系统设施的情况下完成的,但GHC可以并且确实使用多个操作系统线程来实际执行选择要运行的任何代码,至少在使用线程运行时系统时是这样。

它应该都是绿色线程

如果您的程序是用单线程RTS编译的(或者更确切地说是链接的),那么所有绿色线程都在一个操作系统线程中运行。如果您的程序是用多线程RTS编译(链接)的,那么(默认情况下)每个CPU核心的一个OS线程上会调度任意数量的绿色线程


据我所知,在任何一种情况下,阻塞I/O调用都只会阻塞一个绿色线程。其他绿色线程应该完全不受影响。

这并不像问题所暗示的那么简单。Haskell是一种比您遇到的大多数语言都更有能力的编程语言。特别是,从内部角度看似乎阻塞的
IO操作可以按照“启动非阻塞IO操作、挂起线程、在覆盖多个Haskell线程的IO管理器中等待该IO操作完成、在IO设备就绪后将线程排队以恢复”的顺序执行

有关通过标准全局IO管理器提供该功能的api,请参阅和

是否使用绿色线程与此模式基本无关。IO操作可以编写为在后台使用非阻塞IO,并进行适当的多路复用,同时向用户呈现阻塞接口

不幸的是,这也不是那么简单。事实上,操作系统的局限性阻碍了我们的发展。直到最近(我想5.1内核可能是昨天发布的吧?),Linux还没有为非阻塞磁盘操作提供良好的接口。当然,有些事情看起来应该奏效,但实际上并不太好。因此,在GHC中,磁盘读/写是实际的阻塞操作。(也不仅仅是在linux上。GHC没有很多开发人员支持它,所以很多东西都是用在linux上工作的相同代码编写的,即使有其他替代方案。)

但它甚至不像“网络操作是隐藏的,非阻塞的,磁盘操作是阻塞的”那么简单。至少可能不是。我不知道,因为在非线程运行时很难找到文档。我知道线程运行时实际上维护了一个单独的线程池,用于执行标记为“安全”的FFI调用,从而防止它们阻止绿色线程的执行。我不知道非线程运行时是否也是如此


但是对于你的例子,我可以说-假设
getURL
使用标准的网络库(不管怎么说,这是一个假设函数),它将在后台通过适当的多路复用来执行非阻塞IO。因此,即使没有线程化运行时,这些操作也将是真正并发的。

即使使用线程化运行时,默认值也是一个操作系统线程,而不是多个。您必须通过RTS选项或编程方式明确要求更多线程。@DanielWagner我认为他们将默认值更改为每个CPU核心一个线程(因为人们对默认值只有一个线程感到困惑)。哦,嗯,在8.4.3中似乎不是这样。目前我还没有一个8.6。*,虽然8.6个.*发行注释中没有一个提到“线程”一词,也有FFI线程池要考虑,这不是由<代码> -n>代码>参数计算的。<代码> GETURL做出任何基于FFI的调用吗?@ JBRYRMAN,我不确定您是否正确。非线程IO子系统有一点边缘案例的怪癖,它不太正确,而且可能不正确。我不记得在哪里了。@jberryman GHC不使用先发制人的多任务处理。只要小心,您就可以编写纯Haskell代码(甚至不需要
IO
),从而阻止相同功能上的所有其他线程。它是协作的,默认情况下(这几乎总是足够频繁的)在内存分配或每次函数调用(如果打开)时都会产生收益。线程运行时和非线程运行时都是如此。(错误地删除了我的评论,对不起!)@DanielWagner很欣赏这一点,但在我看来,究竟该如何称呼GHC的模型是有争议的。在实践中,它感觉和用户期望它的行为是先发制人的(尽管实现有点粗略)。编译器在特定点插入产量以获得(近似)时间切片是一个实现细节,你不认为吗?@jberryman抽象地认为它是先发制人的,这是一个很好的抽象,直到抽象泄漏的那一刻。然后,突然之间,您的紧密、不分配的循环变成了一个bug而不是一个特性,并且您不安全的FFI调用使您的程序变慢而不是变快。
do a1 <- async (getURL url1)
  a2 <- async (getURL url2)
  page1 <- wait a1
  page2 <- wait a2