С# ждет Task.Delay(1000); требуется только 640 мс

Итак, это просто объяснить, но я искал и не могу найти никого с той же проблемой. Моя проблема возникла в долгосрочной периодической задаче, которая выполнялась чаще, чем я хотел. Кажется, каждый Task.Delay() я создаю и ожидаю возвращения примерно в 65% от задержки, указанной в ms.

Проблема сводилась к следующей строке кода, возвращающейся примерно в 640-660 мс (по сравнению с визуальной студией. Я установил точку останова на этой строке кода и следующую за ней, и она сказала, что прошло)

await Task.Delay(1000); 

На двух других машинах базовая база IDENTICAL работает нормально. Не только это простое утверждение выше, но и периодические задачи. Есть ли настройка где-нибудь, что повлияет на Task.Delay(int millisecondsDelay)? Тип тика, тактовая частота, что угодно, системные часы??? Я в недоумении...

EDIT:

В нижеприведенном фрагменте EtMilliseconds находится где-то от 130-140 мс, что примерно равно ок. 65% ожидаемой продолжительности, указанной выше. Никогда ничего за пределами этого (кроме первого раза во время(), которое не имеет значения).

Long EtMilliseconds;
Stopwatch etWatch = new Stopwatch();
etWatch.Restart();

while (true)
{
  EtMilliseconds = etWatch.ElapsedMilliseconds;
  taskDelay = Task.Delay(200);
  etWatch.Restart();
  await taskDelay;
}

ИЗМЕНИТЬ 2:

Следующий код заставляет EtMilliseconds еще раз 131 мс или около того. Использование Thread.Sleep кажется неэффективным...

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        long EtMilliseconds;
        Stopwatch etWatch = new Stopwatch();
        etWatch.Restart();

        while (true)
        {
            EtMilliseconds = etWatch.ElapsedMilliseconds;
            label.Content = EtMilliseconds.ToString();
            etWatch.Restart();
            Thread.Sleep(200);
        }
    }
}

Этот фрагмент тот же, но использует Task.Delay(200). Этот флажок правильно обновляет метку графического интерфейса (Thread.Sleep не работает), и он равен 131 или 140 мс. Всегда...

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private async void button_Click(object sender, RoutedEventArgs e)
    {
        Task taskDelay;
        long EtMilliseconds;
        Stopwatch etWatch = new Stopwatch();
        etWatch.Restart();

        while (true)
        {
            EtMilliseconds = etWatch.ElapsedMilliseconds;
            label.Content = EtMilliseconds.ToString();
            taskDelay = Task.Delay(200);
            etWatch.Restart();
            await taskDelay;
        }

    }
}

ИЗМЕНИТЬ 3:

Вместо использования DispatcherTimer я все еще получаю примерно 130 мс от своего секундомера .ElapsedMilliseconds... НО здесь странная вещь. Если я также обновляю отображение DateTime.Now(), они увеличиваются примерно на 200 мс (или чуть больше), что я и ожидал. Что за?!?! введите описание изображения здесь

public partial class MainWindow : Window
{

    public long etMilliseconds;
    public Stopwatch etWatch;

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    //  System.Windows.Threading.DispatcherTimer.Tick handler
    //
    //  Updates the current seconds display and calls
    //  InvalidateRequerySuggested on the CommandManager to force 
    //  the Command to raise the CanExecuteChanged event.
    private void dispatcherTimer_Tick(object sender, EventArgs e)
    {
        // Updating the Label which displays the current second
        tBoxCurrTime.Text += DateTime.Now.ToString("yyyy_MMM_dd-hh:mm:ss.fff_tt") + "\n";
        tBoxMilliSecElapsed.Text += etWatch.ElapsedMilliseconds + "\n";
        etWatch.Restart();

        // Forcing the CommandManager to raise the RequerySuggested event
        CommandManager.InvalidateRequerySuggested();
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        etWatch = new Stopwatch();

        //  DispatcherTimer setup
        DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
        dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
        dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 200);
        dispatcherTimer.Start();

        etWatch.Restart();
    }
}

Ответ 1

Просто, чтобы закрыть это, на случай, если у кого-то еще будет аналогичная проблема, я нашел решение своей проблемы. Примерно месяц назад мои эффекты Windows 7 "Aero" начали двигаться ОЧЕНЬ медленно. В поисках решения этой проблемы я нашел следующее:

http://www.tomshardware.com/forum/57307-63-slow-motion-aero-transitions

Решение, которое я использовал, которое находится в ссылке выше, заключается в следующем:

Запустите CMD как администратор. Введите "bcdedit/set useplatformclock true" и нажмите enter (без кавычек). Перезагруженный компьютер и мои проблемы были решены.

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

Спасибо за все твои усилия, ребята, я ценю это!

Гэри

Ответ 2

Я излагаю ответ, основанный на экспериментах, сделанных до сих пор.

В классе Stopwatch используется счетчик производительности Windows. Я часто читал, что на некоторых системах он возвращает неточные данные. Это, похоже, происходит со старыми версиями аппаратного обеспечения и/или более старой версии операционной системы.

Например, время может перескакивать в зависимости от того, на каком ядре вы работаете. Вероятно, это не проблема, потому что ваши тайминги постоянно отключены. Но это пример проблем с такими данными времени.

Я предполагаю, что Visual Studio также использует Stopwatch.

Это также соответствует тому факту, что проблема возникает только на вашем компьютере. Другие машины, вероятно, имеют разное время аппаратного обеспечения.

Попробуйте следующее:

var sw = Stopwatch.StartNew();
var startDateTime = DateTime.UtcNow;

Thread.Sleep(200);

sw.Stop();
var endDateTime = DateTime.UtcNow;

И опубликуйте результаты. Мое предсказание заключается в том, что версия Stopwatch неверна, а версия на основе DateTime показывает чуть более 200 мс.

Насколько мне известно, ядро ​​Windows использует источник времени, который DateTime.UtcNow использует для него собственные таймеры и задержки. AFAIK - это аппаратное прерывание, которое по умолчанию тикает на частоте 60 Гц и заставляет таймер обновлять глобальную временную переменную с такой скоростью. Это означает, что даже если DateTime.UtcNow неверно, он должен соответствовать Thread.Sleep. Но мы знаем, что DateTime.UtcNow является правильным. В противном случае вы заметили значительный системный дрейф системы.

Возможно, вы можете попробовать отключить аппаратное обеспечение, которое обеспечивает Windows с помощью высокочастотного счетчика. Stopwatch.IsHighResolution должен возвращать true сейчас и должен стать ложным, если вы отключите эту часть оборудования. На моей машине он называется "HPET" в диспетчере устройств.