Haskell 哈斯克尔:为什么usleep&x2B;线程编译选项比threadDelay更精确?

Haskell 哈斯克尔:为什么usleep&x2B;线程编译选项比threadDelay更精确?,haskell,raspberry-pi,usleep,Haskell,Raspberry Pi,Usleep,我用Haskell在Raspberry Pi上编写了一个测试程序,它能播放一首悦耳的曲调 在连接到GPIO引脚的蜂鸣器上 以下是我使用的导入: import qualified Control.Concurrent as C import qualified Control.Monad as M import System.IO import qualified System.Posix.Unistd as P 以下是通过写入来切换pin的函数 /sys/class/gpio/gpio16/v

我用Haskell在Raspberry Pi上编写了一个测试程序,它能播放一首悦耳的曲调 在连接到GPIO引脚的蜂鸣器上

以下是我使用的导入:

import qualified Control.Concurrent as C
import qualified Control.Monad as M
import System.IO
import qualified System.Posix.Unistd as P
以下是通过写入来切换pin的函数 /sys/class/gpio/gpio16/value文件:

changePin2 :: Handle -> String -> Int -> IO ()
changePin2 handle onOff delay = do
  pos <- hGetPosn handle
  hPutStr handle (onOff ++ "\n")
  hFlush handle
  hSetPosn pos
  P.usleep delay
  --C.threadDelay delay

blinkOn2 :: Handle -> Int -> IO ()
blinkOn2 handle delay = do
  changePin2 handle "1" delay
  changePin2 handle "0" delay
当我第一次尝试它时,我使用了threadDelay,它听起来很糟糕。声音很低, 这表明延迟的时间比预期的要长,所有的音符听起来都差不多。 使用usleep函数可以大大改善情况。 最后,在使用ghc编译时添加了线程选项,使声音更加清晰

ghc -threaded buzzer1t.hs
我不明白为什么这两种方法都能改进它,如果有人知道这会有很大帮助的话

谷歌搜索似乎揭示了usleep和friends在操作系统层面上是延迟,而threadDelay则是延迟 仅适用于Haskell程序本身中的线程。threadDelay似乎也是 尽管在这种情况下,usleep显然是最好的做法,但还是推荐了一种更好的做法 卓越。

我认为这是一个良好的开端:

GHC注意:threadDelay是一个更好的选择。如果没有-threaded选项,usleep将阻止所有其他用户线程。即使使用多线程选项,usleep本身也需要一个完整的OS线程。threadDelay没有这两个缺点

进一步扩展:GHC运行时将用户线程多路复用到系统线程上。默认运行时仅使用单个OS线程,而不管有多少个用户线程。大多数对外部代码的阻塞调用都是这样编写的,即当它们在外部代码中时,它们会重新调度当前Haskell用户线程,这允许与Haskell代码并发执行。这意味着,例如,即使只有一个OS线程的默认运行时也可以处理同时执行IO的多个用户线程

在这个世界上,实际上阻塞操作系统线程被认为是一种有点恶意的行为
threadDelay
仅将当前线程标记为在指定的时间到期之前不可运行。这对运行时系统更友好,因为它释放了底层OS线程

当您使用线程运行时,您会得到多个OS线程来执行用户线程,但是获取一个线程而不释放它仍然有点不友好。除此之外,它还阻止垃圾回收器运行(它会等待,直到它可以在已知的安全点暂停所有用户线程,这样它就不会同时损坏正在使用的内存),并且如果您添加额外的程序来弥补丢失的并发性,那么OS线程的内存要比用户线程多得多


所以对于大多数软件来说,
threadDelay
是一个更好的公民。但它也有缺点。线程不一定立即恢复。它可以在给定的时间进行调度,但这并不意味着它实际运行。这仍然取决于其他线程。这几乎肯定是您遇到问题的原因——从可运行到实际运行的额外延迟
usleep
专门针对遇到阻碍的情况。这似乎是在需要时使用它的一个很好的理由。

这是一个很好的详细答案,说明了何时/为什么选择其中一个,以及权衡。线程化选项仍然需要我做更多的研究,但我认为我走的方向是正确的。如果你试图使用调度延迟来驱动扬声器,那么你可能想要一个实时操作系统,而不是在普通桌面操作系统上运行的通用编程语言中进行精确计时。但是祝你好运@MathematicalOrchid:我不得不对这个回答哈哈大笑!这并不是因为它错了,而是因为它让我想起了21世纪初第一次学习Linux,遇到Linux用户时,他们会说“为什么有人会使用像Windows这样不稳定的操作系统?”,好像他们真的对此感到惊讶。你可以放心,它只是一个压电蜂鸣器在一个试验板上,我只是想在Haskell中做他们在100个不同的raspberry pi/python教程中做的事情。是的,这不是一个完全严肃的回答。我只是指出,尝试使用通用线程调度算法进行精确计时是。。。我们可以说是危险的
ghc -threaded buzzer1t.hs