-
[RESOLVED] Seperating timeControl from GUI Thread using BGW
I'm currently using a TimeControl Sub that is run on the GUI thread (.i.e. this Sub is called from a Menu.Click routine)
Code:
Public Sub TimeControl
Static LastSimTime As Integer = Enviroment.TickCount
Dim ActualTime As Integer
Try
ActualTime = Enviroment.TickCount
If LastSimTime + StepIntervall >= ActualTime Then
Simulation 'This Sub that starts all the BackGroundWorkers(BGW) that do the calculation of movements
LastSimTime = ActualTime
End If
If StartStop = True Then
ExecuteAfterPause(StepIntervall, New MethodInvoker( Adressof TimeControl) ' This Sub calls itself as long as StartStop is True
End If
Catch ex As Exeption
'.....
End Try
End Sub
Using it that way the simulation works and is displayed by the GUI.
However sole GUI actions (like measurment of distance) migth delay the TimeControl. For that reason I would like to use another BGW.
In the Menu.Click this BGW is just started as "BGW.RunWorkerAsync()" and the TimeControl Sub is called in the BGW_DoWork Sub.
Using it that way the GUI is only updated when moving the mouse. I even tried to put "MainForm.BGW.ReportProgress(1)" in the TimeControl and
Code:
Private Sub BGW_ProgressChanged....
Me.Update
End Sub
(with BGW.WorkerReportsProgress = True)
with no change.
In my understanding the .ProgressChanged is handeld on the GUI threat, therefore I did expect the Me.Update the show the actual data as does the movement of the mouse.
Where am I wrong????
-
Re: Seperating timeControl from GUI Thread using BGW
Try Me.Refresh, but only for information. Me.Update causes the form to redraw invalid regions using Invalidate. That doesn't mean that they WILL be redrawn, it only queues them up so that the UI can redraw when it gets around to it. Refresh forces an immediate re-draw. It's a bit nasty, in this case, because it will cause the entire form to redraw, which may well be more work than you'd really like it to do. If there is just one or two controls to redraw, then call the .Refresh method for those controls, which will reduce the total drawing.
However, if this DOES work, then there is something else going on. Assuming that those BGW events are happening at reasonable intervals (greater than 100ms would be reasonable, though it could also be shorter intervals), then even .Invalidate should work. So, if .Refresh works, then change it to .Invalidate and try that. You then get these outcome alternatives:
1) .Invalidate works, but .Update doesn't: For whatever reason, none of the controls has become invalidated, so the form saw no reason to repaint anything as a result of .Update.
2) .Refresh works, but .Invalidate doesn't: The UI thread is not processing messages. This can only happen if your loop is causing the UI thread to be constantly busy without giving it a chance to pump the message queue. This seems possible because you appear to be running a de facto busy loop in the TimeControl method. It looks like you are calling a method in an infinite loop. You want to take a look at the stack in this case, too, because it seems like it should be filling up steadily.
3) None of them work: See #2. After all, the ReportProgress event is just another message that gets added to the message queue, so if you aren't letting the UI process messages, it won't process this one, either.
-
Re: Seperating timeControl from GUI Thread using BGW
Thank you for taking the time for such an elaborate answer.
Me.Refresh doesn't work either.
Some further remarks on your answer:
-Busy Loop-
The "ExecuteAfterPause" is using the "Pauser" code posted by jmcilhinney http://www.vbforums.com/showthread.p...e-to-Your-Code and further dicussed by dday9 http://www.vbforums.com/showthread.p...ameloop+pauser. In other words, AFAIK this is not a busy loop. I had that code (without the BGW for time) running for houres without any Stack-Error.
-Nothing gets Invalidated-
I do use .Invalidate(Region) for all changes on the main picturebox. All the other BGWs just do calculate positions and do invalidate the old and new region. All the drawing is done in the respective .Paint event.
-The UI thread is not processing messages
Why would the controls on the UI be updated when moving the mouse? More excatly, when moving the mouse on the main picturebox, this picturebox gets updated,the controls that get changed within the MouseMove routine get updated AND controls which are NOT called in that routine get updated. (The sole exception to that would be pictureboxes within UserControls that don't get their drawing update, but for them I'm using "Marshal.WriteByte (...." the write directly into the memory of the controls Bitmap).
The code inside the MouseMove just calculates distance-variables (which are drawn in the. Paint event), .Invalidates the regions for them and does some updates of other textboxes.
-
Re: Seperating timeControl from GUI Thread using BGW
Ok, I see that it is not a busy loop, which is good.
The odd behavior of the mouse had me puzzled anyways. What is it about moving the mouse that would cause other things to refresh? It was acting as if the messagequeue wasn't pumping messages until the mouse message came along. That doesn't make much sense, but .Refresh would have worked had that been some form of the actual situation.
If .Refresh isn't working, but .Refresh is being called, then it would appear that at the time that .Refresh is called, there isn't anything new to show. After all, if .Refresh is being called, then the display is showing what it thinks is the current data. You feel that it is showing the wrong thing, obviously, so why is that? Either the data is wrong or your expectations are, and that's going to be tricky to figure out.
One thing: Since you have a ProgressChanged event with something in it, can you verify that it is being raised at reasonable intervals? The simplest check would be to put a breakpoint in the event, but that would only show one event. An alternative might be to add a List(of date) and write Date.Now to the list in the event handler, then run it for a bit, then pause and check the list to see whether stuff is being written. That might provide some kind of a clue.
-
Re: Seperating timeControl from GUI Thread using BGW
Quote:
Originally Posted by
Shaggy Hiker
You feel that it is showing the wrong thing, obviously, so why is that? Either the data is wrong or your expectations are, and that's going to be tricky to figure out.
My expectations are
-TimeDisplay(TextBox) is showing increasing time
-PositionDisplay(Textbox) is showing actual postion of MainObject (simulated aircraft)
-Movement of MainObject is shown on the map
Without movement of mouse no update on all of them, with movement they update to current data.
The logging test showed the ProgressChanged being called several times each second, since to FPS is set to 10 and I see it being called up to 10 times, that looks as expected to me.
-
Re: Seperating timeControl from GUI Thread using BGW
Further testing, although the ProgressChanged is being called for each frame, the PictureBox.Paint isn't called if the mouse isn't moving within that picturebox.
The picturebox is invalidated during each frame at least for the movement of the MainObject
-
Re: Seperating timeControl from GUI Thread using BGW
Refresh would force that Paint, yet you said that Refresh didn't solve anything. It is kind of acting like the ReportProgress isn't happening on the UI thread. You could probably test that by storing the SynchronizationContext before starting the BGW, then checking against the current SynchContext in the ReportProgress event.
As you might have figured, I'm guessing here. I'm not sure what's going on, though you do seem to be narrowing in on an answer.
-
Re: Seperating timeControl from GUI Thread using BGW
Are you sure the BGW is the right tool for the job?. The BGW should do some task, report any progress needed then end. I'm actually quite interested in what you are doing. I'm curious to know about the use of MethodInvoker with the BGW, have you actually printed what thread ID your working on? I don't think you should be swallowing exception when unknown problems happen.
-
Re: Seperating timeControl from GUI Thread using BGW
Am I sure that the BGW is the rigth tool? NO I'm not, that the reason for calling!
I'm running a game-type application. It can create own objects and recieve objects from elsewhere, all objects are moved (locally using their reported velocities and by update messages) and the MainObject can show all objects in different views (i.e. sensors like radar etc.).
It's all running based on time. More explanation needed?
The MethodInovker is used to create a manged GameLoop (as suggested by dday9 in the above mentitoned thread).
Since I never used SynchronizationContext, I have to do some more reading on that before i'm able to do checks in here.
Firstly I'll trsy the suggestion to log the thread-ID, which I haven't done yet.
@Shaggy Hiker. It's not only you who is guessing. Being in the guessing state is the reason for this threat. Thanks for helping out.
-
Re: Seperating timeControl from GUI Thread using BGW
@Ident
The check for ThreadID did show the problem. The ThreadID where I'm calling the BGW.RunWorkerAsync() is not the same as the ThreadID where the BGW is coming back with its .ProgressChanged. But WHY? In humble understanding you call a BGW from the UI thread, let it run on a different thread and it will come back to the UI thread with .ProgressChanged and .Completed.
BTW: I'm not "swallowing exception", I just didn't edit the Error-Logging code in the thread. And no, there was no error logged for that problem.
-
Re: Seperating timeControl from GUI Thread using BGW
I found the problem, it is caused by the recursive GameLoop!
I did logg the ThreadIDs when calling the BGW (ID 1), in .ProgressChanged Event (ID 2) and in the RunWorkerCompleted Event (Id 1!).
And the RunWorkerCompleted Event is ONLY being raised after the first .ProgressChanged Event.
Why would the .ProgressChanged be on another ThreadID??
-
Re: Seperating timeControl from GUI Thread using BGW
What is the thread ID for the BGW itself (the background thread)? Is it 2, or is it 3? In other words, is the ProgressChanged event being raised on the same thread as the background process, or is it being raised on yet another thread?
-
Re: Seperating timeControl from GUI Thread using BGW
Only the RunWorkerCompleted is run on the GUI thread (This one I do NOT need!)!
The ProgressChanged is run the the same Thread where the TimeControl is run, which is not the GUI thread, more exactly each time the TimeControl is run once the ProgressChanged is run on the same ThreadID, in the next run they have a different ThreadID.
-
Re: Seperating timeControl from GUI Thread using BGW
Are you sure TimeControl is not being run on the UI making it look like the controls are not updating though.
-
Re: Seperating timeControl from GUI Thread using BGW
Since the logging showed different .ManagedThreadIDs for the GUI and the TimeControl I am sure.
-
Re: Seperating timeControl from GUI Thread using BGW
The main problem was in the usage of the Default instance of the GUI when calling the .ReportProgress. Solving that I got the ProgressChanged run on the GUI thread.
However, the usage of the recursive GameLoop resulted in multiple uses of the BGW that had already been completed on the first run, which in turn raised an error (I had to change the MethodInvoker into a Deleagate in ordr to accept the BackGroundworker as an argument).
My options are now:
-A- Let the TimeControl run without a BGW on the GUI threat (no busy loop used)
-B- Run the TimeControl on a BGW and use as busy loop in the GameLoop( which would make to purpose of using another thread obsolete)
-C- Try to "encapsulate" the current GameLoop in a way that the BGW would not be completed (presently no idea, still thinking.....)
Thanks to both of you for your help.
I'll close this one.
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
I was never clear on why dday wanted to have a steady tick in his game loop. Typical game loops try to run as fast as they possibly can. Therefore, they are constant loops that time themselves. The loop might have these steps:
Do
GatherInput
DoLogic
Move
Draw
Loop
Something like a stopwatch would be used to time each iteration of the loop. The elapsed time would be passed to the methods that needed it, which would progress the scene based on the amount of time since the last iteration. Therefore, if there isn't any input and not much logic, the iteration time might be REALLY fast, like 10ms (resulting in about 100FPS), so the movement would be a very short distance. If the logic was involved, the iteration might be kind of slow, like 50ms (about 20FPS), so the movement for each frame would be larger.
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
I already have the parts of "GatherInput", "DoLogic" and "Move" seperated in BGW. So far my GameLoop was run on the GUI thread, and it was hampered whenever I did some sole GUI-actions. So I reasoned the GameLoop should be on a different thread.
Using your technique, the GameLoop would be a "busy loop", but not a "busy wait" since its not waiting at all. To my understanding a "busy loop" shouldn't be used?
Are you suggesting to use such a "busy loop", or am I misunderstanding?
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
*bangs head, should of spotted MainForm.control. Why is another form/class trying to access a BGW from mainform?
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
One other thing, and im sorry if i have missed it but what exactly is being updated? Label?, textbox? *shrug*
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
Quote:
Originally Posted by
ident
*bangs head, should of spotted MainForm.control. Why is another form/class trying to access a BGW from mainform?
Easy answer, Me.Stupid. But I stopped banging my head. Of course it worked when all the calls to BGW were done from the MainForm thread, but when trying to move the TimeControl into another thread I moved those calls to the other thread as well, which was the BIG mistake I assume.
"Updated"= Picturebox showing actual positions of objects (like a Map-Display), Textboxes showing actual AlphaNumeric data of selected objects (like Lat/Long of object or Playtime). Why *shrug* ???
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
I didn't want to guess more controls, shrug was not at you. Forgive me if this is old ground i want to just get it clear in my head. Your application runs like this.
On a secondary thread do something
update some controls
wait some time
then does same task again
If this is the case then why even use a "busy/non busy" loop?
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
I do need "something" that controls the time, which IMHO can be either done by using a Timer (which would be on the GUI thread) or by using some kind of loop.
Any other suggestion?
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
A timer launching the background thread as a Task? Timers are not particularly precise, but you could use a Stopwatch to measure the true elapsed time and pass that to the Task.
By the way, default instances of forms are thread specific, which is probably what was causing the original problem. There may be a reason to use default instances and threading, but it's not a great idea.
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
Why does it need to be done on the UI thread as this seems to be your worry. The forms.timer tick is performed on the UI, why not use System.Timers class thats tick is raised on a secondary thread.
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
Quote:
Originally Posted by
Shaggy Hiker
Timers are not particularly precise,
Agreed but at best iv only noticed it as a second out after a long time. As far as i know it only calls WM_TIMER message which is simply a message and not high priority. Threaded timers are more accurate. "off top my head comments"
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
System.Timers, each day something new to me.
I'll look into that one, Thanks again.
@Shaggy Hiker: YES, the default instance was the original problem!(post #16)
-
Re: [RESOLVED] Seperating timeControl from GUI Thread using BGW
I thought you had figured that out, but it wasn't clear from the post whether you had recognized that it's a characteristic of default instances or not. It's really a rather weird aspect of them, and one that I often forget about. Nasty little oddity, really.
I believe that the code JMC posted in that link is using a System.Timers.Timer.