views:

136

answers:

1

I have a code engine that plays long WAV files by playing smaller chunks in succession using the waveOutOpen and waveOutWrite API methods. In order to update my UI as the file plays, from the callback function as each buffer completes playing I Invoke a separate thread (because you want to do as little as possible inside the callback function) that calls a method in my form.

The form contains a class level EventHandler that handles a method within which I update UI elements with new information. In the form method called from the waveOutWrite callback function, I use the Invoke method like so:

if (_updatedisplay == null)
{
    // UpdateDisplay contains code to set control properties on the form
    _updatedisplay = new EventHandler(UpdateDisplay);
}
Invoke(_updatedisplay);

Everythings works, but it appears that once in a while there is a noticeable lag or delay in the updating of the UI elements. This is easy to see because I am using the UpdateDisplay method to drive an animation, so the delays appear as "hiccups" where the sprite appears to freeze for a split second before it jumps to its expected position.

Is it possible that there is sometimes a long (maybe 10-15 milliseconds) delay involved in cross-thread communication like this? If so, what's a better way of handling something like this?

Update: by the way, I'm definitely not sure that Invoke is the culprit here. Another possibility is a lag between when a chunk of audio finishes playing and when the callback function actually gets called.

Update 2: per itowlson's suggestion, I used a System.Diagnostics.Stopwatch to benchmark the lag between Invoke and method call. Out of 1156 measurements, I got 1146 at 0ms, 8 at 1ms, and 2 at 2ms. I think it's safe to say Invoke is not my culprit here.

+3  A: 

Yes, there can be an arbitrarily long delay. Invoke works by sending a Windows message to the target control, so it will only get processed when the target thread pumps messages. If the thread is already processing a message, and that processing takes time, then there may be an appreciable delay before the thread pumps its next message and thereby processes the Invoke.

A better way may be to call BeginInvoke. This doesn't avoid the potential delay in the UI thread processing the message, but it saves your calling thread from being blocked while waiting for the UI thread to pump messages. However, this may not help in your scenario where it sounds like it's the busy-ness of the UI thread which is causing glitching in the animation.

Update in response to your update: Note all I'm saying here is that there could be an arbitrarily long delay, not that there will be a noticeable delay or that this is definitely the cause of your delay. 10-15ms does seem an unusually long time for an application to spend in message processing unless there's something really intensive happening on the UI thread, so you're certainly wise to consider alternative causes!

itowlson
I just tried using BeginInvoke, but it's still hiccuppy. Also, I get the same problem a while back trying to run an animation with callbacks from a hi-res timer, and got the same kind of glitches. I wonder if this is something fundamental with .Net UIs?
MusiGenesis
No, it's fundamental to *any* UI at least as far as Windows goes. The message architecture in Windows Forms is nothing new, it's the same way even a C++ application works in Windows. If you're trying to update a progress bar or something you shouldn't be invoking across threads. Instead use interlocked writes to a progress variable which the UI thread reads at certain intervals.
Josh Einstein
This *sounds* like *maybe* the animation component is not being a good citizen in terms of pumping messages, but I'm hesitant to speculate about what's going on here especially since you're not sure Invoke is the issue. Try capturing the time when BeginInvoke was called and when UpdateDisplay started executing: that might help identify whether the cross-thread invoke is indeed the problem. (You'll need to use high-res timings.)
itowlson
Well, it's not `Invoke`, and the rendering of each frame of animation takes less than 1 ms, so it's not that. My two remaining culprits are latency in the waveOutWrite callbacks themselves, or it's a problem of synchronizing my frame rates with the screen refresh rate. I can't do anything about either one, unfortunately.
MusiGenesis