5

Я разрабатываю Windows 10 Universal App в C#/Xaml, я использую await Task.Delay (delayInMilliseconds), чтобы приостановить метод для заданного времени. Мой сценарий несколько в реальном времени, поэтому он очень чувствителен к изменениям времени, и мне нужно убедиться, что, когда я приостановил метод, скажем, 8 миллисекунд, он будет приостановлен на 8 миллисекунд. Я заметил, что фактический временной интервал, для которого ** Task.Delay ** приостанавливает метод, отличается от того, что передается как параметр задержки, в течение 1 до 30 мс, причем длина «отклонения» различна для каждого вызова. Итак, когда я хочу спать в течение 8 миллисекунд, моя система спит от 9 до 39 миллисекунд, и это полностью разрушает мой сценарий. Итак, мой вопрос: какой лучший способ заменить ** Task.Delay ** и добиться хорошей точности? В настоящее время я использую этот метод:Точность задачи.Delay

public static void Delay1(int delay) 
    { 
     long mt = delay * TimeSpan.TicksPerMillisecond; 
     Stopwatch s = Stopwatch.StarNew(); 
     while (true) 
     { 
      if (s.Elapsed.TotalMilliseconds > delay) 
      { 
       return; 
      } 
     } 
    } 

но guees он потребляет много ресурсов, то есть 100% от сердечника процессора. Если у пользователя небольшое количество ядер процессора, это будет очень мало.

+1

Task.Delay не является таймером и не предназначен для точного определения времени. Дело не только в том, что он использует System.Threading.Timer (http://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,5fb80297e082b8d6.references), но также несут расходы планируя его продолжение в потоке ThreadPool. Разрешение [разрешение по таймеру составляет 15,6 мс] (http://stackoverflow.com/questions/3744032/why-are-net-timers-limited-to15-ms-resolution), поэтому вы просите о выходе из limits –

+1

Что вы пытаетесь сделать? Зачем вам такая высокая точность? Возможно, есть и другие * способы достижения одного и того же. Я не могу придумать ** любой случай, когда универсальное приложение требует точности на уровне ядра. Вы пытаетесь контролировать анимацию или воспроизведение звука? –

+0

@Panagiotis Kanavos: Да, именно, я пытаюсь контролировать анимацию, воспроизведение звука и использование сети, поэтому мне нужна такая точность. – xcoder37

ответ

4

По msdn это не возможно достичь большей точности в связи с разрешением часов системы:

Этот метод зависит от системных часов. Это означает, что время задержка будет приблизительно равна разрешению системного тактового сигнала, если миллисекунды. Аргумент delay меньше разрешения системных часов , что составляет около 15 миллисекунд в системах Windows .

+0

Вы порекомендовали бы любую точную замену высокого разрешения для этого метода? System.Diagnostics.Stopwatch, например, основан на ** API QueryPerformanceCounter ** (https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx), но единственным решением, которое я придумал до сих пор, является проверка цикла секундомера в цикле, который действительно точен, но тяжелый для ресурсов – xcoder37

+0

MSDN ошибочен; существует много способов добиться большей точности, а не с помощью открытых таймеров системы или вещей, которые зависят от них. –

1

Вы должны использовать таймеры мультимедиа. Они очень точны. Посмотрите здесь: https://social.msdn.microsoft.com/Forums/vstudio/en-US/2024e360-d45f-42a1-b818-da40f7d4264c/accurate-timer

+0

Это не поможет, потому что Task.Delay уже использует эти таймеры. Метод, предложенный для мультимедийных таймеров, устарел и заменен на CreateTimerQueueTimer. Task.Delay использует System.Threading.Timer внутри, который ... использует CreateTimerQueueTimer. –

+0

Взгляните на мой ответ. Ошибка составляет от 3 до 4 микросекунд. Здесь: http://s23.postimg.org/qf4ke0dgr/solution.png –

1

Кажется, я нашел sattisfactory решения:

нового System.Threading.ManualResetEvent (ложный) .WaitOne (delayInMilliseconds) вместо ждет Task.Delay (delayInMilliseconds);

Я использовал следующий код, чтобы проверить оба метода:

async void Probe() 
    { 
     for (int i = 0; i < 1000; i++) 
     { 
      // METHOD 1 
      await Task.Delay(3); 
      // METHOD 2 
      new System.Threading.ManualResetEvent(false).WaitOne(3); 
     } 
    } 

Код выше должен занять ровно 3 секунды, чтобы выполнить. С МЕТОДОМ 2 это заняло около 3300 мс, так что eror составляет 0,3 мс за звонок. Приемлемо для меня. Но МЕТОД 1 tok около 15 секунд (!) Для выполнения, что дает совершенно неприемлемую ошибку для моего сценария. Я только задаюсь вопросом, что такое использование процессора с помощью метода 2, надеюсь, что он не использует опрос, документация говорит что-то об использовании сигналов, но, к сожалению, это автоматически не означает, что опрос не используется в другом месте.

+0

Хм, это интересно. Оба должны использовать один и тот же механизм синхронизации в ядре. Вы действительно не изменили ничего другого, например, повышение частоты таймера? Браузер Chrome увеличивает его до 1 мс, например. – usr

+1

Я не могу воспроизвести это. Это не будет работать надежно. – usr

+0

Этот искусственный тест ничего не говорит о том, как это будет работать в анимационном сценарии, как вы упомянули в комментариях. Вместо того, чтобы пытаться обрабатывать каждый отдельный кадр или звуковой пакет, используйте анимации и переходы, чтобы определить, когда каждая анимация должна начинаться, заканчиваться и т. Д. –

1

После трудолюбия я нашел решение для спящего потока в течение определенного времени, а ошибка - всего от 3 до 4 микросекунд на моем процессоре Core 2 Duo 3,00 ГГц. Вот он: enter image description here

Вот код. (Код C# также указан в конце.) Используйте "ThreadSleepHandler":

Public Class ThreadSleepHandler 
Private Stopwatch As New Stopwatch 
Private SpinStopwatch As New Stopwatch 
Private Average As New TimeSpan 
Private Spinner As Threading.SpinWait 
Public Sub Sleep(Time As TimeSpan) 
    Stopwatch.Restart() 
    If Average.Ticks = 0 Then Average = GetSpinTime() 
    While Stopwatch.Elapsed < Time 
     If Stopwatch.Elapsed + Average < Time Then 
      Average = TimeSpan.FromTicks((Average + GetSpinTime()).Ticks/2) 
     End If 
    End While 
End Sub 
Public Function GetSpinTime() As TimeSpan 
    SpinStopwatch.Restart() 
    Spinner.SpinOnce() 
    SpinStopwatch.Stop() 
    Return SpinStopwatch.Elapsed 
End Function 
End Class 

Вот пример кода:

Sub Main() 
    Dim handler As New ThreadSleepHandler 
    Dim stopwatch As New Stopwatch 
    Do 
     stopwatch.Restart() 
     handler.Sleep(TimeSpan.FromSeconds(1)) 
     stopwatch.Stop() 
     Console.WriteLine(stopwatch.Elapsed) 
    Loop 
End Sub 

Для C# программистов здесь есть код (Я преобразовал этот код, но я не уверен):

static class Main 
{ 
public static void Main() 
{ 
    ThreadSleepHandler handler = new ThreadSleepHandler(); 
    Stopwatch stopwatch = new Stopwatch(); 
    do { 
     stopwatch.Restart(); 
     handler.Sleep(TimeSpan.FromSeconds(1)); 
     stopwatch.Stop(); 
     Console.WriteLine(stopwatch.Elapsed); 
    } while (true); 
} 
} 
public class ThreadSleepHandler 
{ 
private Stopwatch Stopwatch = new Stopwatch(); 
private Stopwatch SpinStopwatch = new Stopwatch(); 
private TimeSpan Average = new TimeSpan(); 
private Threading.SpinWait Spinner; 
public void Sleep(TimeSpan Time) 
{ 
    Stopwatch.Restart(); 
    if (Average.Ticks == 0) 
     Average = GetSpinTime(); 
    while (Stopwatch.Elapsed < Time) { 
     if (Stopwatch.Elapsed + Average < Time) { 
      Average = TimeSpan.FromTicks((Average + GetSpinTime()).Ticks/2); 
     } 
    } 
} 
public TimeSpan GetSpinTime() 
{ 
    SpinStopwatch.Restart(); 
    Spinner.SpinOnce(); 
    SpinStopwatch.Stop(); 
    return SpinStopwatch.Elapsed; 
} 
} 

Примечание: «ThreadSleepHandler» является небезопасным. Вы не можете использовать один «ThreadSleepHandler» для нескольких потоков.

Первое время сна будет недостаточно точным.

+0

К сожалению, это вообще не отвечает на вопрос. OP хотел управлять * анимациями *, выполняя несколько действий в секунду. Ваш код помещает поток в сон в течение целой секунды. В масштабе 1 с разница между таймером или замораживанием и оттаиванием нити не очень значительна. –

+0

Итак, ему нужно создать таймер, который должен поднять событие в точное время? –

+0

Ваше решение опросает Секундомер в цикле, который тяжел для процессора. Я проверил монитор ресурсов, и использование ЦП заметно увеличилось, когда я поставил поток в режим сна, используя этот метод. Фактически, ваше решение сводится к методу Delay1(), который находится в моем коде, сжатом в моем вопросе. – xcoder37

0

В моих тестах я обнаружил, что DispatcherTimer с интервалом 20 мс будет обеспечивать достаточно плавную анимацию, если вы должны сделать это в коде. Выполнение этого в XAML - еще одна альтернатива, как уже упоминалось.

Смежные вопросы