【问题标题】:"Emulating" Application.Run using Application.DoEvents“模拟” Application.Run 使用 Application.DoEvents
【发布时间】:2010-03-30 17:35:36
【问题描述】:

我遇到了麻烦。我正在尝试使用 Application.DoEvents 模拟调用 Application.Run...这听起来很糟糕,然后我也接受了我的问题的替代解决方案...

我必须像 Application.Run 那样处理消息泵,但我需要在消息处理之前和之后执行代码。这是主要的重要sn-p代码。

// Create barrier (multiple kernels synchronization)
sKernelBarrier = new KernelBarrier(sKernels.Count);

foreach (RenderKernel k in sKernels) {
    // Create rendering contexts (one for each kernel)
    k.CreateRenderContext();
    // Start render kernel kernels
    k.mThread = new Thread(RenderKernelMain);
    k.mThread.Start(k);
}

while (sKernelBarrier.KernelCount > 0) {
    // Wait untill all kernel loops has finished
    sKernelBarrier.WaitKernelBarrier();
    // Do application events
    Application.DoEvents();
    // Execute shared context services
    foreach (RenderKernelContextService s in sContextServices)
        s.Execute(sSharedContext);

    // Next kernel render loop
    sKernelBarrier.ReleaseKernelBarrier();
}

这段代码由 Main 例程执行。实际上,我有一个内核类列表,它们在单独的线程中运行,这些线程处理用于在 OpenGL 中呈现的表单。我需要使用屏障来同步所有内核线程,这非常有效。 当然,我需要在主线程(Main 例程)中处理 Form 消息,对于创建的每个 Form,我确实调用 Application.DoEvents() 来完成这项工作。

现在我必须修改上面的 sn-p 以获得一个通用的表单(简单对话框),而不像 Application.Run 那样消耗 100% 的 CPU 调用 Application.DoEvents()。

目标应该是让上面的sn-p在消息到达时处理,只在必要时发出渲染(释放屏障),而不是试图获得最大FPS;应该有可能切换到严格循环以尽可能多地渲染。

这怎么可能?

注意:上面的 sn-p 必须在 Main 例程中执行,因为 OpenGL 上下文是在主线程上创建的。在单独的线程中移动 sn-p 并调用 Application.Run 是非常不稳定和错误的......

【问题讨论】:

    标签: c# forms opengl message-pump


    【解决方案1】:

    在循环中调用 Application.DoEvents() 没有任何根本性的错误。这就是 Form.ShowDialog() 所做的。它采取对策来确保用户不会遇到麻烦:它禁用对话框以外的所有窗口,因此用户无法退出应用程序或再次启动对话框。

    您需要创建自己的,设置一个全局标志,指示您的主窗口已关闭,以便在从您下方拉出地毯时,您可以立即退出循环,而无需调用任何代码。

    您需要让处理器来避免 100% 的 CPU 负载。最简单的方法是调用 Thread.Sleep(1)。以我在this thread 中的回答为例。

    【讨论】:

    • 吹毛求疵和 - 愚蠢:有很多关于 Thread.Sleep(0)Thread.Sleep(1) 下降的讨论..
    【解决方案2】:

    不要这样做 - 这东西非常复杂,我敢肯定你自己实现它只会遇到麻烦。

    你不能用Application.AddMessageFilter()来实现你需要的吗?

    【讨论】:

    • 我可以处理特定的消息来发出渲染,但我想知道如何在循环中切换以尽可能多地渲染。从单独的线程重复发送消息? (这意味着使用 Application.Run 而不是 sn-p,这使得屏障的使用更加困难)
    • 只发布(而不是发送!)您处理的消息,然后在处理程序结束时再次重新发布相同的消息。这会产生一种无限循环,同时保持其他一切运行。
    【解决方案3】:

    如果你要构建这样的消息循环,你应该 PInvoke 实际的 Win32 消息处理函数(这是 Application.Run 在幕后所做的一切——它有一个名为 UnSafeNativeMethods 的内部类,它映射一个一堆)。

    如果您不需要在消息调用之间继续处理 - 换句话说,如果您的线程在不主动处理消息时休眠是安全的 - 然后从 User32.dll 绑定 WaitMessage 并将其放入在这样的循环中:

    while (WaitMessage())
    {
      Application.DoEvents();
    }
    

    如果您需要更多帮助,请告诉我。我现在正在重新安装 VS,或者我会发布一个示例应用程序来展示如何进行绑定和 PInvoke。

    【讨论】:

    • 我想避免 PInvoke(或者至少,对于 WIN32、X11 和 OSX 有特定的 PInvoke)... sleep 听起来不太好,不是吗?
    • 啊。如果您正在尝试编写跨平台的东西,那么循环中的 Application.DoEvents() 与您将获得的一样好,不幸的是。 Win32、X11 和 OSX 之间的消息泵设计非常不同,虽然 Mono 框架很好地隐藏了它并使它看起来像 Win32 架构,但它并没有暴露任何细粒度的控制来工作及其抽象。
    猜你喜欢
    • 2011-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-31
    • 1970-01-01
    相关资源
    最近更新 更多