"Emulating" Application.Run using Application.DoEvents
I'm getting in trouble. I'm trying to emulate the call Application.Run using Application.DoEvents... this sounds bad, and then I accept also alternative solutions to my question...
I have to handle a message pump like Application.Run does, but I need to execute code before and after the message handling. Here is the main significant snippet of code.
// C开发者_如何学Pythonreate 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();
}
This snippet of code is execute by the Main routine. Pratically I have a list of Kernel classes, which runs in separate threads, these threads handle a Form for rendering in OpenGL. I need to synchronize all the Kernel threads using a barrier, and this work perfectly. Of course, I need to handle Form messages in the main thread (Main routine), for every Form created, and indeed I call Application.DoEvents() to do the job.
Now I have to modify the snippet above to have a common Form (simple dialog box) without consuming the 100% of CPU calling Application.DoEvents(), as Application.Run does.
The goal should be to have the snippet above handle messages when arrives, and issue a rendering (releasing the barrier) only when necessary, without trying to get the maximum FPS; there should be the possibility to switch to a strict loop to render as much as possible.
How could it be possible?
Note: the snippet above must be executed in the Main routine, since the OpenGL context is created on the main thread. Moving the snippet in a separated thread and calling Application.Run is quite unstable and buggy...
There isn't anything fundamentally wrong with calling Application.DoEvents() in a loop. That's what Form.ShowDialog() does. It takes counter-measures to ensure the user cannot get into trouble: it disables all windows other than the dialog so the user cannot exit the application or start the dialog again.
You'll need to create your own, set a global flag that indicates that your main window was closed so you can immediately exit the loop without calling any more code when the rug is pulled out from under you.
You'll need to yield the processor to avoid 100% CPU load. The easiest way to do that is by calling Thread.Sleep(1). Check my answer in this thread for an example.
Don't do it - this stuff is pretty complex and I'm pretty sure you get nothing but trouble for implementing it yourself.
Can't you use Application.AddMessageFilter()
to implement what you need?
If you're going to build a message loop like this, you should PInvoke the actual Win32 message handling functions (which is all that Application.Run is doing behind the scenes -- it has an internal class called UnSafeNativeMethods that maps a bunch of them).
If you don't need to keep processing between message calls -- in other words, if it's safe for your thread to sleep when it's not actively handling a message -- then bind WaitMessage from User32.dll and put it in a loop like:
while (WaitMessage())
{
Application.DoEvents();
}
If you need more help let me know. I'm in the middle of a VS reinstall right now or I'd post a sample application showing how to do the binding and PInvoke.
精彩评论