Calling Thread.Sleep
can sometimes be a code-smell. Without seeing exactly how you are using it I cannot advocate its use in your scenario. But, that aside, the following class might solve your problem. Though, I cannot personally think of a situation where I would ever use this.
public class Sleeper
{
private TimeSpan remaining = TimeSpan.Zero;
public TimeSpan GetRemaining()
{
lock (this)
{
if (remaining > TimeSpan.Zero)
{
Monitor.Pulse(this);
Monitor.Wait(this);
}
return remaining;
}
}
public void Sleep(TimeSpan timeout)
{
lock (this)
{
if (remaining > TimeSpan.Zero)
{
throw new InvalidOperationException("Sorry, but I'm not safe for multiple sleepers.");
}
remaining = timeout;
DateTime expires = DateTime.UtcNow.Add(remaining);
bool complete = remaining <= TimeSpan.Zero;
while (!complete)
{
bool complete = Monitor.Wait(this, remaining);
remaining = expire - DateTime.UtcNow;
if (remaining <= TimeSpan.Zero)
{
complete = true;
}
if (complete)
{
remaining = TimeSpan.Zero;
}
Monitor.Pulse(this);
}
}
}
}
And then use it like this.
public static void Main()
{
var sleeper = new Sleeper();
Task.Factory.StartNew(
() =>
{
while (true)
{
sleeper.Sleep(TimeSpan.FromMinutes(1);
}
});
while (true)
{
Console.WriteLine("Press ENTER to get time remaining...");
Console.ReadLine();
TimeSpan remaining = sleeper.GetRemaining();
Console.WriteLine("Remaining = " + remaining.ToString());
}
}
Again, there are only a few scenarios where I can imagine that this might be useful. I suppose a more sophisticated variation of a latch that is focused more on timing might be useful. The principals of how it would work would be very similar to what I created above. You might call it a "timed latch" which could also report its due time to any interested callers.