对于Haskell中的其他线程和Tchan,HOpenGL的行为如何?

对于Haskell中的其他线程和Tchan,HOpenGL的行为如何?,opengl,haskell,concurrency,Opengl,Haskell,Concurrency,我正在为一个相当复杂的视频游戏做一些概念验证工作,我想用Haskell编写,使用HOpenGL库。我从编写一个模块开始,该模块实现了基于客户机-服务器事件的通信。当我尝试将它连接到一个简单的程序以在屏幕上绘制点击时,我的问题就出现了 事件库使用一个tchan列表作为优先级队列进行通信。它返回与服务器绑定和客户端绑定消息相对应的“out”队列和“in”队列。使用forkIO在单独的线程中发送和接收事件。在没有OpenGL部件的情况下对事件库进行测试,结果表明它能够成功地进行通信。下面是我用来测试它

我正在为一个相当复杂的视频游戏做一些概念验证工作,我想用Haskell编写,使用HOpenGL库。我从编写一个模块开始,该模块实现了基于客户机-服务器事件的通信。当我尝试将它连接到一个简单的程序以在屏幕上绘制点击时,我的问题就出现了

事件库使用一个tchan列表作为优先级队列进行通信。它返回与服务器绑定和客户端绑定消息相对应的“out”队列和“in”队列。使用forkIO在单独的线程中发送和接收事件。在没有OpenGL部件的情况下对事件库进行测试,结果表明它能够成功地进行通信。下面是我用来测试它的代码:

--客户端连接到本地主机上的服务器,优先级队列中有3个优先级
do{(outQueue,inQueue)原子级$writeThing outQueue(lookupPriority x)x)
(重复(单击(从积分2)(从积分4)))
}
这将产生预期的输出,即大量的发送和接收事件。我认为问题不在于事件处理库

代码的OpenGL部分在displayCallback中检查传入队列中的新事件,然后调用事件的关联处理程序。我可以通过displayCallback捕获一个事件(Init事件,它只是清除屏幕),但是在这之后什么都不会被捕获。以下是相关代码:

atomicly$PQ.writeThing in queue(Events.lookupPriority Events.Init)Events.Init
主回路
渲染PQUE=
do事件返回事件
putStrLn$“获取事件”
(Events.lookupHandler事件Events.Client)事件
德国劳埃德船级社
过剩的灌木
因此,我关于为什么会发生这种情况的理论是:

  • 显示回调正在阻止重试时的所有发送和接收线程
  • 没有正确返回队列,因此客户端读取的队列与OpenGL部件读取的队列不同
这一切发生的原因还有其他吗

完整的代码太长,无法在这里发布,虽然不太长(每个100行下有5个文件),但是都在GitHub上

编辑1:
客户机在HOpenGL代码的主函数中运行,如下所示:

main=
做args
do让e=事件{Just e'->e'}
处理程序e
返回()
德国劳埃德船级社
过剩的灌木
过剩。后期再也看不到任何东西
我试过它,有没有postRedisplay,它只适用于它。现在的问题是,这将CPU固定在100%,因为它是一个繁忙的循环。在edit4中,我建议去掉显示回调。我还在想办法

一张便条,因为我还没有提到它。任何想要构建/运行代码的人都应该这样做:

$ghc-threaded-package GLUT helloworldOGL.hs-o helloworldOGL
$ghc server.hs-o服务器
--不管是这样还是那样,我通常做0.0.0.0
美元/服务器“localhost”3
美元/服务器“0.0.0.0”3
$./helloworldOGL“localhost”3
编辑6:解决方案
解决办法!随着线程的发展,我决定在OpenGL代码中创建一个线程来检查事件,如果没有任何事件,则阻塞,然后调用处理程序,然后再调用postRedisplay。这是:

checkEvents pqueue=永远$
do事件返回事件
putStrLn$“获取事件”
(Events.lookupHandler事件Events.Client)事件
过剩。后期再也看不到任何东西
显示回调仅为:

render=GLUT.swapBuffers

它可以工作,它不会100%地与CPU挂钩,事件会得到及时处理。我之所以在这里发帖,是因为如果没有其他答案,我不可能做到这一点,而且当这些答案都非常有用时,我觉得接受@Laar的回答很糟糕,因为他代表的级别较低。

一个可能的原因可能是使用线程

OpenGL使用线程本地存储作为其上下文。因此,所有使用OpenGL的调用都应该从同一个OS线程进行。HOpenGL(以及OpenGLRaw)是OpenGL库的一个相对简单的绑定,没有为这个“问题”提供任何保护或解决方法

另一方面,您是否正在使用
forkIO
创建一个轻量级haskell线程。此线程不保证保持在同一操作系统线程上。因此,RTS可能会将其切换到线程本地OpenGL上下文不可用的另一个OS线程。要解决这个问题,有一个
forkOS
函数,它创建一个绑定的haskell线程。此绑定haskell线程将始终在同一OS线程上运行,因此其线程本地状态可用。有关这方面的文档可以在的“绑定线程”部分找到,也可以在那里找到

编辑:


对于当前的测试代码,这个问题不存在,因为您没有使用-线程。(删除了不正确的推理)

您的
render
函数只会被调用一次,因为只有在有新的绘图内容时才会调用display回调。要请求重画,您需要致电

GLUT.postRedisplay Nothing

它接受一个可选窗口参数,或者当您传递
Nothing
时,发出“当前”窗口重画的信号。您通常从
idleCallback
timerCallback
调用
postRedisplay
,但也可以在
render
结束时调用它,请求立即重画。

您使用什么来创建和运行客户端,尤其是向ghc/runhaskell/ghci传递什么标志?已启动,您可以复制结果链接以获取指向各种函数的链接。但是。。。他没有链接到线程运行时,因此只有一个OS线程可以开始,而且他正在
forkIO
ing的东西无论如何都不会调用OpenGL!