可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.