CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 7 of 7
  1. #1
    Join Date
    Jun 2011
    Location
    Buenos Aires, Argentina
    Posts
    130

    Post Non-blocking Delay

    Hi, I'm trying to have my app wait for a while, just doing nothing, without blocking my GUI from being responsive to keyboard and mouse events. Using Thread.Sleep(X) will block, so I'm trying to have a second thread to wait.

    I'm either a very bad searcher, or there isn't much out there on the subject!! Everyone just goes for Thread.Sleep(X) ...
    (I did find this article though, which I found very interesting)

    What I'm trying is to have a second thread do the "sleeping" while the main thread "does events" until the waiting is done. For that, I have the following class:

    Code:
    using System.Threading;
    
    public static class Timers
    {
    	static double delayCount;
    
    	public static void Delay(double miliseconds)
    	{
    		delayCount = miliseconds;
    
    		BackgroundWorker worker = new BackgroundWorker();
    		worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    		worker.RunWorkerAsync();
    
    		while (worker.IsBusy)
    		{
    			Application.DoEvents();
    			Thread.Yield();
    		}
    		worker.Dispose();
    	}
    
    	static void worker_DoWork(object sender, DoWorkEventArgs e)
    	{
    		System.Windows.Forms.Timer waiter = new System.Windows.Forms.Timer();
    		waiter.Interval = (int)delayCount;
    		waiter.Tick += new EventHandler(waiter_Tick);
    		waiter.Start();
    		while (waiter.Enabled) { }
    		waiter.Dispose();
    	}
    	static void waiter_Tick(object sender, EventArgs e)
    	{
    		((System.Windows.Forms.Timer)sender).Stop();
    	}
    }
    The problem with it is that the Tick event never happens, thus the timer never stops, the worker never finishes its work, and finally my app ends up waiting forever.

    I'm creating the timer inside the worker because if Ï do it from the Delay function it will block. Add a mouse move event to any object on the main form and you'll be able to check if it blocks very easily. A big blank form with a label and an update mouse coordinates event will do.

    Does anyone know where to find information on this? Or ideas on how to do it?
    Thank you in advance!

  2. #2
    Join Date
    Jun 2011
    Location
    Buenos Aires, Argentina
    Posts
    130

    Re: Non-blocking Delay

    Ok... using a different Timer will get the Elapsed event to fire, but will still block GUI updates!

    Code:
    static void worker_DoWork(object sender, DoWorkEventArgs e)
    {
    	System.Timers.Timer waiter = new System.Timers.Timer((int)delayCount);
    	waiter.Elapsed += new System.Timers.ElapsedEventHandler(waiter_Elapsed);
    	waiter.Start();
    
    	while (waiter.Enabled) { Thread.Yield(); }
    	waiter.Dispose();
    }
    static void waiter_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
    	((System.Timers.Timer)sender).Stop();
    }
    Why is it blocking my main thread? If the BackgroundWorker is, by default, a new thread created entirely to split tasks...
    Is Application.DoEvents() not enough to continue operting normally?

  3. #3
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Non-blocking Delay

    One reason it doesn't work is you are spinning the UI thread while waiting>

    bad juju....
    Code:
    		while (worker.IsBusy)
    		{
    			Application.DoEvents();
    			Thread.Yield();
    		}
    At any rate, here's the deal - change the way you think about wanting to wait in the UI thread and instead have your UI thread respond to an event. If you need to wait for something in another thread, disable some of the UI controls, fire off a thread that does work, and create an event handler indicates the thread has finished its work. BackgroundWorker has Cancel and Completed events that work great for this.

    In the UI thread when you receive the completed event (or cancel) event, reenable the controls and/or update data.

    Using the above scenario, the UI thread isn't actually waiting on anything. It's using the combination of disabling controls and events to prevent the user from doing much while the work is being done. The user still remains in control, so if they wanted to they could still move the form, cancel the operation and so on.

  4. #4
    Join Date
    Jun 2011
    Location
    Buenos Aires, Argentina
    Posts
    130

    Re: Non-blocking Delay

    Hi Arjay, thank you for your comments. What do you mean by spinning?

    I actually already changed all the code to perform as a state machine with a Timer calling and checking the state of whatever it is I'm doing. If idle, just exit and do nothing. The next Timer.Tick event will call the state machine again until I have something to do. That way I only do minimal processing every X msec.

    Eventually I will change the state machine to work completely on events, as opposed to be called periodically by a Timer. That should not only work better, but also more accurately, since the (imperceptible, but existing) delay between the moment something happened that will give me things to do and the time the Timer fires the processing would go down to zero.

    That would also allow me to change the big switch I have to process events and instead deal with function pointers, targeting whatever state I want to move next and use it when the next event fires. That would be particularly useful for the "just waiting" scenario.

    I guess when I get that done it should be working a lot better. In any case, I'm just a bit surprised that there is no easier way of "just waiting" without consuming 100% CPU or Sleeping the application.

  5. #5
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Non-blocking Delay

    Quote Originally Posted by Nikel View Post
    Hi Arjay, thank you for your comments. What do you mean by spinning?
    Spinning is spinning. The following code spins.
    Code:
    while (worker.IsBusy)
    {
    	Application.DoEvents();
    	Thread.Yield();
    }
    Add a trace statement and watch what happens.
    Code:
    while (worker.IsBusy)
    {
    	Application.DoEvents();
    	Thread.Yield();
    	Console.WriteLine( "I am spinning" );
    }
    Quote Originally Posted by Nikel View Post
    I actually already changed all the code to perform as a state machine with a Timer calling and checking the state of whatever it is I'm doing. If idle, just exit and do nothing. The next Timer.Tick event will call the state machine again until I have something to do. That way I only do minimal processing every X msec.

    Eventually I will change the state machine to work completely on events, as opposed to be called periodically by a Timer. That should not only work better, but also more accurately, since the (imperceptible, but existing) delay between the moment something happened that will give me things to do and the time the Timer fires the processing would go down to zero.

    That would also allow me to change the big switch I have to process events and instead deal with function pointers, targeting whatever state I want to move next and use it when the next event fires. That would be particularly useful for the "just waiting" scenario.
    I don't believe you understood what I said. I never mentioned the word 'timers'. If you use a timer, then you are probably polling something and that goes against the concept of an event.

    Quote Originally Posted by Nikel View Post
    I guess when I get that done it should be working a lot better. In any case, I'm just a bit surprised that there is no easier way of "just waiting" without consuming 100% CPU or Sleeping the application.
    There is an easy way of having the UI get signalled when the worker thread is finished - it's called set the UI thread an event when it's finished doing the work. Handle the event in the UI thread. Done.

    Did you understand what I was talking about when I mentioned the BackgroundWorker class? Have you looked for any examples in msdn on how to use that class?

  6. #6
    Join Date
    Jun 2011
    Location
    Buenos Aires, Argentina
    Posts
    130

    Re: Non-blocking Delay

    I agree a Timer would go againts events, but that's just the point, waiting! It not an event I need, it's just wasting time. I'd be more than happy to hear how you would implement a wait of X seconds without using a Timer...

    As for the BGW, that's exactly how this thread started. Read the first two posts. I was working with BGW.DoWork, thinking that would give my main thread the freedom to respond to every single user interaction, but found that it will still freeze my app for a time, even if it was supposed to be a different Thread. So basically what I did IS set the UI thread to respond to an event, but, not done! It was freezing the main thread.

  7. #7
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Non-blocking Delay

    Quote Originally Posted by Nikel View Post
    As for the BGW, that's exactly how this thread started. Read the first two posts. I was working with BGW.DoWork, thinking that would give my main thread the freedom to respond to every single user interaction, but found that it will still freeze my app for a time, even if it was supposed to be a different Thread. So basically what I did IS set the UI thread to respond to an event, but, not done! It was freezing the main thread.
    Then you coded it up wrong.

    If you added an event handler to the bgw completed event in the UI thread, there is no way that the bgw thread will interact with the UI thread unless you coded it up that way.

    If you want to be shown what is wrong with your implementation, then post a sample project that repros the problem.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured