System.Diagnostics.Stopwatch возвращает отрицательные числа в свойствах Elapsed ... - PullRequest
19 голосов
/ 17 июня 2009

Это нормальное поведение, когда секундомер может возвращать отрицательные значения? Пример кода ниже может быть использован для его воспроизведения.

 while (true)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            sw.Stop();

            if (sw.ElapsedMilliseconds < 0)
                Debugger.Break();

        }

Единственное место, где я могу воспроизвести отрицательные числа, - это моя виртуальная машина (размещенная в Hyper-V на 8-ядерном компьютере)

Ответы [ 4 ]

17 голосов
/ 17 июня 2009

Это ошибка . Кажется, он не уделяет этому много внимания, так что я бы предложил продолжить этот отчет.

Обходное решение , по-видимому, игнорирует отрицательные значения:

long elapsedMilliseconds = Math.Max(0, stopwatch.ElapsedMilliseconds);
11 голосов
/ 22 сентября 2011

Я декомпилировал класс Stopwatch в .NET 2.0 и .NET 4.0 с помощью Reflector, а затем сравнил различия, чтобы увидеть, как это было исправлено. Помимо добавления нового метода Restart, это все, что я нашел для разницы:

public void Stop()
{
    if (this.isRunning)
    {
        long num2 = GetTimestamp() - this.startTimeStamp;
        this.elapsed += num2;
        this.isRunning = false;
// THE NEXT 4 LINES ARE NEW IN .NET 4.0:
        if (this.elapsed < 0L)
        {
            this.elapsed = 0L;
        }
    }
}

Таким образом, они устанавливают значение 0 в методе Stop, если значение отрицательное. Есть еще баги ИМХО:

  1. Что если пользователь прочитает какое-либо из свойств Elapsed перед остановкой секундомера? Вы все еще можете получить отрицательное значение.
  2. Сброс на 0 неверен. Некоторое время должно было пройти, даже если это было всего несколько микросекунд!
  3. Он ничего не делает для обработки необычно больших положительных прошедших значений, о которых я и другие сообщили.

РЕДАКТИРОВАТЬ: К сожалению, # 2 и # 3 находятся за пределами возможностей .NET Framework:

Вот суть проблемы: из MSDN на QueryPerformanceCounter , который представляет собой API, используемый классом секундомера:

На многопроцессорном компьютере не должно иметь значения, какой процессор называется. Тем не менее, вы можете получить разные результаты на разных процессоры из-за ошибок в базовой системе ввода / вывода (BIOS) или уровень аппаратной абстракции (HAL). Чтобы указать сродство процессора для поток, используйте функцию SetThreadAffinityMask.

Не просто используйте секундомер .NET 4.0 и предположите, что проблема исправлена. Это не так, и они ничего не могут с этим поделать, если вы не хотите, чтобы они испортили вашу привязанность к нитям. Из документации класса Секундомер:

На многопроцессорном компьютере не имеет значения, какой процессор поток работает. Однако из-за ошибок в BIOS или оборудовании Слой абстракции (HAL), вы можете получить различные результаты синхронизации на разные процессоры. Чтобы указать привязку процессора к потоку, используйте метод ProcessThread.ProcessorAffinity.

0 голосов
/ 07 декабря 2011

Единственный обходной путь, который я нашел для получения правильного истекшего времени в виртуальной машине, - это (VB):

Dim tstart AS DateTime = Now
...executing code...
Dim telapsed = (Now - tstart).TotalMilliseconds
0 голосов
/ 22 мая 2011

Разрешение «Закрыто как исправлено» для Microsoft Connect Bug должно означать, что они исправили его в .NET 4. Я запустил код репро на своем рабочем столе и виртуальной машине в течение нескольких минут и получил нет отрицательных значений.

...