.net конструкция для цикла while с тайм-аутом - PullRequest
20 голосов
/ 08 июля 2011

Я обычно использую цикл while, который продолжает пытаться выполнить некоторую операцию, пока либо операция не завершится успешно, либо не истечет время ожидания:

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

Я знаю, что есть несколько способов реализовать это, но сутьв том, что я несколько раз пытался выполнить какую-либо операцию со сном до успеха или я спал слишком долго в совокупности.

Есть ли встроенный класс .net / метод / etc, чтобы спасти меня от переписывания этого шаблонаповсюду?Возможно, ввод - это Func (из bool) и время ожидания?

Редактировать
Спасибо всем, кто внес свой вклад.Я выбрал подход sleep (), потому что он был наименее сложным, и я полностью против сложности =) Вот мой (все еще нуждается в проверке) имплиментация:

 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;
    }

Ответы [ 8 ]

22 голосов
/ 08 июля 2011

Вы можете заключить свой алгоритм в метод:

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;
}

, а затем:

if (RetryUntilSuccessOrTimeout(() => SomeTask(arg1, arg2), TimeSpan.FromSeconds(10)))
{
    // the task succeeded
}
13 голосов
/ 03 мая 2017

Вы можете использовать SpinWait.SpinUntil

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

bool spinUntil = System.Threading.SpinWait.SpinUntil(() => job.IsDisposed, TimeSpan.FromSeconds(5));
5 голосов
/ 08 июля 2011

Вам действительно не нужно использовать Sleep(), чтобы дождаться завершения задач.Делая это, вы тратите в среднем 500 мс после выполнения задачи.

Вы должны быть в состоянии сделать это детерминистически, используя Task Parallel Library, см., Например, здесь .

В этом примере показано, как использовать метод Wait или его эквивалент в классе Task для ожидания одной задачи.Также показано, как использовать статические методы WaitAll и WaitAny для ожидания нескольких задач.

4 голосов
/ 08 июля 2011

Я не знаю, есть ли что-то существующее, но я думаю, вы могли бы создать метод, который бы принимал тайм-аут и функцию определения успеха. Примерно так:

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;
}

или, может быть, ваша функция может быть более "устойчивой", и вы можете связать ее с гибкими аргументами:

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;
}
2 голосов
/ 08 июля 2011

Другие упоминали методы синхронизации потоков, которые позволили бы вам подождать, пока какое-либо задание не будет завершено.Однако, если вы хотите продолжать опрашивать каждую секунду, как вы делаете, вы можете обернуть этот метод следующим образом:

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.");
}
1 голос
/ 28 февраля 2018

Первое решение - использовать SpinWait

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

Другое решение - использовать Task.Wait ()

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

Оберните ваш цикл в процедуру, которая возвращает значение bool

private bool LoopProcedure()
{
   bool success = false
   while( ( !success )
   {
      // do some stuff ...
   }
   return success;
}
1 голос
/ 08 июля 2011

Вы можете абстрагировать, немного сократить свой код и обобщить время ожидания:

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;
}
0 голосов
/ 08 июля 2011

Вам нужно использовать Thread.Join . От MSDN,

Блокирует вызывающий поток до поток завершается, продолжая выполнить стандартные COM и SendMessage насосное.

Если вы хотите подождать, пока не истечет указанное время, используйте метод Thread.Join (TimeSpan) . .NET 3.0, 3.5, только 4.0.

Блокирует вызывающий поток до поток завершается или указан время истекло , продолжая при этом выполнить стандартные COM и SendMessage насосное.

Этот туториал по использованию Threads в C # поможет вам.

...