.net construct for while loop with timeout

2019-04-18 05:37发布

问题:

I commonly employ a while loop that continues to try some operation until either the operation succeeds or a timeout has elapsed:

bool success = false
int elapsed = 0
while( ( !success ) && ( elapsed < 10000 ) )
{
     Thread.sleep( 1000 );
     elapsed += 1000;
     success = ... some operation ...     
}

I know there a couple of way to implement this, but the basic point is that I repeatedly try some operation with a sleep until success or I've slept too long in aggregate.

Is there a built-in .net class/method/etc to save me from re-writing this pattern all over the place? Perhaps input is an Func(of bool) and the timeout?

Edit
Thanks to all who contributed. I opted for the sleep() approach because it was the least complicated and I'm totally anti-complexity =) Here's my (still needs to be tested) implimentation:

 public static bool RetryUntilSuccessOrTimeout( Func<bool> task , TimeSpan timeout , TimeSpan pause )
    {

        if ( pause.TotalMilliseconds < 0 )
        {
            throw new ArgumentException( "pause must be >= 0 milliseconds" );
        }
        var stopwatch = Stopwatch.StartNew();
        do
        {
            if ( task() ) { return true; }
            Thread.Sleep( ( int )pause.TotalMilliseconds );
        }
        while ( stopwatch.Elapsed < timeout );
        return false;
    }

回答1:

You could wrap your algorithm in a method:

public bool RetryUntilSuccessOrTimeout(Func<bool> task, TimeSpan timeSpan)
{
    bool success = false;
    int elapsed = 0;
    while ((!success) && (elapsed < timeSpan.TotalMilliseconds))
    {
        Thread.Sleep(1000);
        elapsed += 1000;
        success = task();
    }
    return success;
}

and then:

if (RetryUntilSuccessOrTimeout(() => SomeTask(arg1, arg2), TimeSpan.FromSeconds(10)))
{
    // the task succeeded
}


回答2:

You could use SpinWait.SpinUntil

See https://msdn.microsoft.com/en-us/library/dd449238(v=vs.110).aspx

bool spinUntil = System.Threading.SpinWait.SpinUntil(() => job.IsDisposed, TimeSpan.FromSeconds(5));


回答3:

You really should not have to use Sleep() to wait for tasks to complete. You waste an average of 500ms after the task has completed by doing this.

You ought to be able to do this deterministically using Task Parallel Library, see here for example.

This example shows how to use the Wait method, or its equivalent in the Task class, to wait on a single task. It also shows how to use the static WaitAll and WaitAny methods to wait on multiple tasks.



回答4:

I don't know that there's any existing thing, but I would think you could create a method to that would accept the timeout and the success-determination function. Something like this:

public static bool KeepTrying(int timeout, Func<bool> operation)
{
    bool success = false;
    int elapsed = 0;
    while ((!success) && (elapsed < timeout))
    {
        Thread.Sleep(1000);
        elapsed += 1000;
        success = operation();
    }
    return success;
}

or maybe your Function could be more "robust" and you could couple it with flexible arguments:

public bool KeepTrying(int timeout, Func<object[], bool> operation, params object[] arguments)
{
    bool success = false;
    int elapsed = 0;
    while ((!success) && (elapsed < timeout))
    {
        Thread.Sleep(1000);
        elapsed += 1000;
        success = operation(arguments);
    }
    return success;
}


回答5:

Others have mentioned thread synchronization techniques, which would allow you to wait until some task is finished. However, if you want to continue polling every second like you are doing, you can wrap that method like this:

void Main()
{
    Timeout(() => {return false;});
}

public void Timeout(Func<bool> action, int timeout)
{
    bool success = false;
    int elapsed = 0;
    while( ( !success ) && ( elapsed < timeout ) )
    {
         Thread.Sleep( 1000 );
         elapsed += 1000;
         success = action();
    }
    Console.WriteLine("timed out.");
}


回答6:

You can abstract shorten your code a bit and generalize the Timeout:

int timer = 0;
while (!SomeOperation(...) && Timeout(ref timer, 1000, 10000));

public bool Timeout(ref int timer, int increment, int maximum)
{
    timer += increment;
    Thread.Sleep(increment);

    return timer < maximum;
}


回答7:

The first solution is to use SpinWait

SpinWait.SpinUntil(() => LoopProcedure(), 1000);

The other solution is to use Task.Wait()

var task = Task.Run(() => LoopProcedure()); 
task.Wait(1000);

Wrap your loop into a procedure that return bool value

private bool LoopProcedure()
{
   bool success = false
   while( ( !success )
   {
      // do some stuff ...
   }
   return success;
}


回答8:

You need to use Thread.Join. From MSDN,

Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping.

If you want to wait until a specified time elapses, then use the Thread.Join (TimeSpan) method. .NET 3.0, 3.5, 4.0 only.

Blocks the calling thread until a thread terminates or the specified time elapses, while continuing to perform standard COM and SendMessage pumping.

This tutorial on how to use Threads in C# will help you.