-2

I am writing a library that communicates with several WCF endpoints. In many cases, I have to wait for a particular state or condition on the remote service. I have written a simple polling wait loop which looks something like this:

private bool WaitForTrue(Func<bool> action, TimeSpan waitTime, TimeSpan checkInterval)
{
    DateTime end = DateTime.UtcNow + waitTime;
    while (DateTime.UtcNow < end)
    {
        if (action())
        {
            return true;
        }
        Thread.Sleep(checkInterval);
    }

    return action();
}

After reading several posts about how Thread.Sleep is a sign of a poorly designed program, I'm reevaluating every place I've used it. I'm struggling with the best way to replace its usage in this wait loop. I've seen recommendations to use Task.Delay, but since I have to block execution I think I would have to use Task.Delay(checkInterval).Wait() which doesn't seem to provide the typical advantages of tasks.

I'm aware of the advantages of using a callback/event-based WCF method, rather than polling, but unfortunately I am unable to modify the service endpoints to expose this kind of functionality.

My library does not make use of any multi-threading, but I would like it to be friendly to consumers that may wish to multi-thread their applications.

Is replacing Thread.Sleep with Task.Delay.Wait() the way to go, or are there better ways to implement a blocking wait loop?

Community
  • 1
  • 1
BJ Myers
  • 6,617
  • 6
  • 34
  • 50
  • Why not have a timer and execute the function on its event? – Ewan Apr 02 '15 at 16:04
  • @Ewan I've seen that suggestion as well, but I'm having trouble visualizing how that would work. Would you mind posting an answer with an example? – BJ Myers Apr 02 '15 at 16:46
  • So you are polling a remote service and that service has no way of notifying you? – usr Apr 02 '15 at 17:09
  • @usr That's correct, the service doesn't support any kind of callback mechanism. In most cases, I'm making a call to the service that will cause it to (eventually) change state, but I have to query the service again to see what the current state is. – BJ Myers Apr 02 '15 at 18:11

2 Answers2

1

You can avoid Thread.Sleep by waiting on a mutex that never gets signalled. For example, snag yourself a Mutex and use one of the WaitOne overloads that takes a timespan

private bool WaitForTrue(
    Func<bool> action, TimeSpan waitTime, TimeSpan checkInterval)
{
    using(var mutie = new Mutex())
    {
        DateTime end = DateTime.UtcNow + waitTime;
        while (DateTime.UtcNow < end)
        {
            if (action())
            {
                return true;
            }
            mutie.WaitOne(checkInterval);
        }    
        return action();
    }
}

I'm not convinced doing this actually means you're a better programmer than ones that use Thread.Sleep...

(After a little research) It appears Task.Delay releases the current thread and then restarts execution at a later date on (possibly) a different thread. That's definitely better (releasing a thread) than sleeping (locking the thread). If you're already using Tasks during execution, and don't specifically need to block the current thread, then perhaps Task.Delay is your best bet.

  • Why not take it to the extreme and use Rx. Create a Subject to broadcast to subscribers the changes? – John Taylor Apr 02 '15 at 16:18
  • @JohnTaylor Dunno. Overkill? You can always add an answer with the deets. –  Apr 02 '15 at 16:19
  • I am still in the process of understanding Rx. Rx and Tasks lead to some very convoluted thread executes, but they can remove all of the waits and sleeps which is the whole point. – John Taylor Apr 02 '15 at 16:25
  • If Task.Delay did the same thing and was simply better Thread.Sleep would simply always do it the better way. The same thing with the mutex. – usr Apr 02 '15 at 18:23
  • @usr "If Task.Delay did the same thing and was simply better Thread.Sleep would simply always do it the better way." what the hell does this even mean? Did you not read about releasing the current thread (Task.Delay) versus blocking the current thread (Thread.Sleep)? And do you seriously think that the framework team would completely change the behavior of Thread.Sleep just because? I'm utterly confused by this line of thought. –  Apr 02 '15 at 18:36
  • I should not have posted the delay part since you did not write how you would use it. I kind of assumed that you meant Task.Delay.Wait() which is not helping with anything. I take that part of my comment back since it was premature. The comments about the mutex stand. Please consider reverting your retaliation downvote. – usr Apr 02 '15 at 18:37
1

Since you can't be notified by the service when the event that you are looking for happens polling is the best you can do.

If you have a lot of these polling loops running at the same time consider using async waiting to release threads. That requires using, for example,

await Task.Delay(...);

and making the polling method and the entire call chain async.

This is a waste of dev time if there are just a few such polling loops.

Sleeping is fine for purposes of timing which you are doing here.

usr
  • 168,620
  • 35
  • 240
  • 369