我正在努力在Haskell中使用GHC为多核机器编写并发程序.作为第一步,我决定编写一个同时读写IOArray的程序.我的印象是对IOArray的读写操作不涉及同步.我这样做是为了建立一个基线来与使用适当同步机制的其他数据结构的性能进行比较.我遇到了一些令人惊讶的结果,即在很多情况下,我根本没有加速.这让我想知道ghc运行时是否发生了一些低级同步,例如同步和阻塞评估thunks(即"黑洞").这是详细信息......
我在一个程序上写了几个变体.主要思想是我编写了一个DirectAddressTable数据结构,它只是一个提供插入和查找方法的IOArray的包装器:
-- file DirectAddressTable.hs
module DirectAddressTable
( DAT
, newDAT
, lookupDAT
, insertDAT
, getAssocsDAT
)
where
import Data.Array.IO
import Data.Array.MArray
newtype DAT = DAT (IOArray Int Char)
-- create a fixed size array; missing keys have value '-'.
newDAT :: Int -> IO DAT
newDAT n = do a <- newArray (0, n - 1) '-'
return (DAT a)
-- lookup an item.
lookupDAT :: DAT -> Int -> IO (Maybe Char)
lookupDAT (DAT a) i = …Run Code Online (Sandbox Code Playgroud) 我注意到我的一些机器上的GHC.Conc中的threadDelay函数有奇怪的行为.以下程序:
main = do print "start"
threadDelay (1000 * 1000)
print "done"
Run Code Online (Sandbox Code Playgroud)
按预期运行需要1秒钟.另一方面,这个程序:
{-# LANGUAGE BangPatterns #-}
import Control.Concurrent
main = do print "start"
loop 1000
print "done"
where loop :: Int -> IO ()
loop !n =
if n == 0
then return ()
else do threadDelay 1000
loop (n-1)
Run Code Online (Sandbox Code Playgroud)
在我的两台机器上运行大约需要10秒钟,但在其他机器上需要大约1秒钟,正如预期的那样.(我使用'-threaded'标志编译了上述两个程序.)这是一个来自Threadscope的屏幕截图,显示每10毫秒只有一次活动:
另一方面,这是来自我的一台机器的ThreadScope的截图,程序总共需要1秒钟: 
一个类似的C程序:
#include <unistd.h>
#include <stdio.h>
int main() {
int i;
for (i=1; i < 1000; i++) {
printf("%i\n",i);
usleep(1000);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
做正确的事情,即运行'time ./a.out'给出如下输出:
1
2 …Run Code Online (Sandbox Code Playgroud) epoll是否保证在epoll_ctl中为EPOLLIN注册了文件后对epoll_wait的第一个(或正在进行中的)调用,并且在epoll_ctl调用之前文件已经可读的情况下,EPOLLET会立即返回?从我对测试程序的实验来看,答案似乎是肯定的。这里有几个例子来澄清我的问题:
假设我们已经初始化了一个epoll文件efd和一个文件fd 以及以下事件定义:
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
Run Code Online (Sandbox Code Playgroud)
现在考虑这种情况:
fdepoll_ctl (efd, EPOLL_CTL_ADD, fd, &event);epoll_wait (efd, events, MAXEVENTS, -1);现在,步骤3中的呼叫会立即返回吗?以我的经验。这样可以保证吗?
现在考虑第二种情况,扩展第一种情况:
fdepoll_ctl (efd, EPOLL_CTL_ADD, fd, &event);epoll_wait (efd, events, MAXEVENTS, -1);epoll_ctl (efd, EPOLL_CTL_MOD, fd, &event);epoll_wait (efd, events, MAXEVENTS, -1);步骤5中的呼叫是否立即返回?以我的经验。可以保证吗?
关于此问题,epoll手册页并不完全清楚。特别是,手册页建议您在使用边沿触发模式时,应始终从文件读取,直到返回EAGAIN。但是似乎这些注释是假设您不想在文件上等待时就重新注册文件。
epoll的边缘触发期权的目的是什么?是一个相关的讨论。第一个答案的前两个注释似乎证实了我所看到的行为是预期的。
https://gist.github.com/3900742是一个C测试程序,该程序说明了带有管道的epoll的行为似乎与我所描述的一样。