С# ждет 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" в диспетчере устройств.