HOpenGL 对于 Haskell 中的其他线程和 TChan 有何行为?

2023-12-28

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

事件库使用 TChan 列表作为优先级队列进行通信。它返回与服务器绑定和客户端绑定消息相对应的“输出”队列和“输入”队列。发送和接收事件是使用 forkIO 在单独的线程中完成的。在没有 OpenGL 部分的情况下测试事件库表明它通信成功。这是我用来测试它的代码:

-- Client connects to server at localhost with 3 priorities in the priority queue
do { (outQueue, inQueue) <- client Nothing 3
   -- send 'Click' events until terminated, the server responds with the coords negated
   ; mapM_ (\x -> atomically $ writeThing outQueue (lookupPriority x) x)
           (repeat (Click (fromIntegral 2) (fromIntegral 4)))
   }

这会产生预期的输出,即大量发送和接收事件。我认为问题不在于事件处理库。

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

atomically $ PQ.writeThing inqueue (Events.lookupPriority Events.Init) Events.Init
    GLUT.mainLoop

render pqueue =
    do  event <- atomically $
            do  e <- PQ.getThing pqueue
                case e of
                    Nothing -> retry
                    Just event -> return event
        putStrLn $ "Got event"
        (Events.lookupHandler event Events.Client) event
        GL.flush
        GLUT.swapBuffers

所以我关于为什么会发生这种情况的理论是:

  • 显示回调在重试时阻塞所有发送和接收线程。
  • 队列未正确返回,因此客户端读取的队列与 OpenGL 部分读取的队列不同。

还有其他原因导致这种情况发生吗?

完整的代码虽然不是太长,但太长了,无法在这里发布(5 个文件,每个文件不到 100 行),但都在 GitHub 上here https://github.com/RocketPuppy/PupEvents.

Edit 1:
客户端从 HOpenGL 代码的主函数中运行,如下所示:

main =
    do  args <- getArgs
        let ip = args !! 0
        let priorities = args !! 1
        (progname, _) <- GLUT.getArgsAndInitialize
        -- Run the client here and bind the queues to use for communication
        (outqueue, inqueue) <- Client.client (Just ip) priorities
        GLUT.createWindow "Hello World"
        GLUT.initialDisplayMode $= [GLUT.DoubleBuffered, GLUT.RGBAMode]
        GLUT.keyboardMouseCallback $= Just (keyboardMouse outqueue)
        GLUT.displayCallback $= render inqueue
        PQ.writeThing inqueue (Events.lookupPriority Events.Init) Events.Init
        GLUT.mainLoop

当我编译代码时传递给 GHC 的唯一标志是-package GLUT.

Edit 2:
我稍微清理了一下 Github 上的代码。我删除了acceptInput,因为它实际上没有做任何事情,并且客户端代码无论如何也不应该监听它自己的事件,这就是它返回队列的原因。

Edit 3:
我稍微澄清一下我的问题。我从@Shang 和@Laar 那里学到了一些东西,并开始运用它。我将 Client.hs 中的线程更改为使用 forkOS 而不是 forkIO (并在 ghc 中使用 -threaded),看起来事件已成功通信,但在显示回调中未接收到它们。我也尝试过打电话postRedisplay在显示回调结束时,但我认为它不会被调用(因为我认为重试会阻塞整个 OpenGL 线程)。

显示回调中的重试会阻塞整个OpenGL线程吗?如果是这样,将显示回调分叉到新线程中是否安全?我不认为会发生这种情况,因为存在多个事物可能同时尝试绘制到屏幕上的可能性,但我也许可以用锁来处理它。另一种解决方案是将lookupHandler函数返回一个包装在 a 中的函数Maybe,如果没有任何事件,则什么都不做。我觉得这不太理想,因为我基本上会遇到一个繁忙的循环,这是我试图避免的。

Edit 4:
忘了说了,当我做 forkOS 时,我在 ghc 上使用了 -threaded。

Edit 5:
我去测试了我的理论:渲染函数(显示回调)中的重试会阻塞所有 OpenGL。我重写了渲染函数,使其不再阻塞,并且它的工作方式就像我希望的那样。在屏幕上单击一下即可获得两个点,一个来自服务器,一个来自原始单击。这是新渲染函数的代码(注意:它是not在 Github 中):

render pqueue =
    do  event <- atomically $ PQ.getThing pqueue
        case (Events.lookupHandler event Events.Client) of
            Nothing -> return ()
            Just handler -> 
                do  let e = case event of {Just e' -> e'}
                    handler e
                    return ()
        GL.flush
        GLUT.swapBuffers
        GLUT.postRedisplay Nothing

我尝试了使用和不使用 postRedisplay 的方法,并且它仅适用于它。现在的问题是,这会将 CPU 固定在 100%,因为它是一个繁忙的循环。在编辑 4 中,我建议关闭显示回调线程。我还在想办法做到这一点。

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

$ ghc -threaded -package GLUT helloworldOGL.hs -o helloworldOGL
$ ghc server.hs -o server
-- one or the other, I usually do 0.0.0.0
$ ./server "localhost" 3
$ ./server "0.0.0.0" 3
$ ./helloworldOGL "localhost" 3

Edit 6: Solution


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

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

另一方面,你正在使用forkIO创建一个轻量级的 haskell 线程。不保证该线程位于同一操作系统线程上。因此,RTS 可能会将其切换到另一个操作系统线程,其中线程本地 OpenGL 上下文不可用。为了解决这个问题有forkOS函数,它创建一个绑定的 haskell 线程。这个绑定的 haskell 线程将始终在同一个操作系统线程上运行,因此其线程本地状态可用。有关此的文档可以在“绑定线程”部分中找到控制.并发 http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Concurrent.html, forkOS http://hackage.haskell.org/packages/archive/base/latest/doc/html/Control-Concurrent.html#v%3aforkOS也可以在那里找到。

edits:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

HOpenGL 对于 Haskell 中的其他线程和 TChan 有何行为? 的相关文章

随机推荐

  • 如何混合(合并)视频和音频,以便音频在输出视频中循环,以防持续时间太短?

    背景 我需要将视频文件和音频文件合并为一个视频文件 以便 输出视频文件的持续时间与输入视频文件的持续时间相同 输出文件中的音频将仅是输入音频文件的音频 如果太短 则会循环到最后 如果需要可以在最后停止 这意味着一旦音频播放完毕而视频尚未播放
  • PHP cli 命令行 safe_mode 限制

    我正在使用 Kohana 框架 3 0 9 它生成每日日志 如果日志文件是在 CRON 运行脚本的前一天制作的 我想邮寄日志文件 但经过几天的尝试 我无法弄清楚如何在 PHP CLI 模式中推迟 safe mode 当我在网络上运行脚本时
  • Android任务管理器源码

    我想知道是否有任何开源 Android 任务管理器 流程管理器 流程细节器 项目可供我查看 我正在为我的大学项目开发 一个应用程序 它将帮助我了解寻找一个好的任务管理器 有谁知道一些开源项目 我可以在其中阅读或查看源代码 我不需要下载源 谢
  • Rails,选择助手,添加样式

    我正在尝试做这样的事情 select model attribute style gt some style Add style to the select helper在 Rails 中 但它不起作用 来自文档 http api ruby
  • 有条件申请课程的最佳方式是什么?

    假设您有一个在 a 中呈现的数组ul与li对于每个元素和控制器上的一个属性 称为selectedIndex 将课程添加到的最佳方式是什么li与索引selectedIndex在 AngularJS 中 我目前正在 手工 复制li代码并将该类添
  • 我如何像 Cocos2D 一样通过 SpriteKit 倾斜/剪切精灵?

    在Cocos2D x中 CCNode类提供了 skewX 和 skewY 来让我对精灵进行一些扭曲 但是 我在SpriteKit的SKNode中找不到类似的映射 我的游戏使用Flash移植骨骼动画 其中精灵的定位 缩放 旋转和剪切的配置将被
  • WebSocket 服务器不支持 SSL

    我有一个使用网络套接字的工作聊天应用程序 我想更进一步 在我的连接上启用加密 但是当我用 https 服务器切换 http 服务器时 我的连接开始失败 我已经生成了一个在我的所有网站上使用的自签名证书 在同一 TLD 下 这意味着它是通配符
  • 二元运算符“||”的操作数类型错误- 爪哇

    我在一个项目上遇到了麻烦 而且我没有足够的词汇来搜索我遇到的问题 我认为这与Java有关字符的语法有关 除了下面的代码之外 输入被视为上面的字符串并解析为字符 switch accountType case c case C Determi
  • 如何从 Rails 中的 URL 获取查询字符串

    有没有办法在 Rails 中获取传递的 URL 字符串中的查询字符串 我想传递一个 URL 字符串 http www foo com id 4 empid 6 我怎样才能得到id and empid 如果字符串中有 URL 则使用 URI
  • 是否需要“严格使用”Python 编译器?

    存在Python静态分析工具 https stackoverflow com questions 35470 are there any static analysis tools for python 但编译时检查往往与运行时绑定哲学 h
  • Spark配置优先级

    在代码中指定 Spark 应用程序配置之间是否有任何区别或优先级 SparkConf setMaster yarn 并在命令行中指定它们 spark submit master yarn 是的 用户代码中使用 set 函数进行的配置具有最高
  • 如何使用 ReactiveCocoa 3 实现基本的 UITextField 输入 + UIButton 操作场景?

    我同时是 Swift 和 ReactiveCocoa 菜鸟 我想使用 MVVM 和 Reactive Cocoa v3 0 beta 4 框架来实现此设置 以学习新的 RAC 3 框架的基础知识 我有一个文本字段 我希望文本输入包含 3 个
  • Ubuntu 上托盘消息的 Java 外观

    我通过托盘Icon displayMessage 在系统托盘上显示消息 但它们看起来不太好 我尝试将它们的外观和感觉更改为系统标准 但在创建图标之前调用 UIManager setLookAndFeel 并没有改变任何内容 UIManage
  • Github 中的“blob”对应什么?

    下面 URL 中 blob 后面的单词指向给定存储库的 master 分支 https github com celery celery blob master docs django first steps with django rst
  • 如果 vue 组件上的模式关闭,如何重置下拉数据?

    我的情况是这样的 我有两个组件 父组件和子组件 我的父组件是这样的
  • 在 Python 中,避免 __init__ 参数和实例变量使用相同名称的最佳方法是什么?

    初始化实例变量的最佳方法是什么init功能 两次使用同一个名字是不是很糟糕 class Complex def init self real imag self real real self imag imag 在我看来 想出这样的任意替代
  • Android Intent 打开“海量存储活动”

    我需要从我的应用程序中打开 USB 海量存储活动 有没有Intent去做这个 就像是 startActivity new Intent Settings ACTION APPLICATION DEVELOPMENT SETTINGS 您可以
  • 解析 docker-compose 中 extra_hosts 选项中的容器名称

    我需要从另一个容器卷曲我的 API 容器 1 称为nginx容器 2 称为fpm 我需要能够猛击我的fpm容器并卷曲nginx容器 Config docker compose yaml services nginx build contex
  • 如何安装和运行 Firefox 的 puppeteer

    你好 我正在做一些网络自动化 我正在尝试打开一个 url 但在 chrome 控制台中收到数据 URL 错误 因此我正在转向 Firefox 控制台 以解决 chrome 控制台中没有打开数据 url 的问题 问题是 npm install
  • HOpenGL 对于 Haskell 中的其他线程和 TChan 有何行为?

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