Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.72/25: Рейтинг темы: голосов - 25, средняя оценка - 4.72
0 / 0 / 0
Регистрация: 08.02.2017
Сообщений: 2

Таймер срабатывает неверное количество раз

15.03.2017, 17:29. Показов 4855. Ответов 8
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Доброго времени суток, друзья!
Помогите пожалуйста разобраться!
Необходимо производить действие с некоторой периодичностью.
Во всех туториалах пишут, что надо использовать Windows.Forms.Timer, но в таком случае, падает точность, так как всё выполняется в одном процессе и таймер тикает медленнее, чем должен.
Нашёл в сети, что можно использовать Threading.Timer, чтобы действие таймера происходили в другом процессе и ничего ему не мешало.
Нашёл на сайте https://msdn.microsoft.com/ru-... x#Anchor_5 как пользоваться этим таймером, переделал немного под свои нужды, но работает не так как хотелось бы..

В общем вот код:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class StatusChecker
{
    private int invokeCount;
    private int maxCount;
 
    public StatusChecker(int count)
    {
        invokeCount = 0;
        maxCount = count;
    }
 
    // This method is called by the timer delegate.
    public void CheckStatus(Object stateInfo)
    {
        AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;
        Write_to_file();  //запись в файл то из-за чего весь сыр-бор
        ++invokeCount;
 
        if (invokeCount == maxCount)
        {
            // Reset the counter and signal the waiting thread.
            invokeCount = 0;
            autoEvent.Set();
        }
    }
}
 
public void ...
{
///
    var autoEvent = new AutoResetEvent(false);
 
    var statusChecker = new StatusChecker(10000000);
 
    System.Threading.Timer stateTimer = new System.Threading.Timer(statusChecker.CheckStatus, autoEvent, 0, 100);
///
}
Проблема в том, что таймер производит действие (сохраняет в файл) только 6-7 секунд, то есть, если задать периодичность 0,5 секунд, сделает 12-14 записей, а если 1 секунду, то 6-7 записей.
Что я делаю неправильно?
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
15.03.2017, 17:29
Ответы с готовыми решениями:

Почему таймер срабатывает несколько раз?
Доброго времени суток, друзья. Нужен таймер, по прошествии которого выполняется программа. Создаю таймер: Timer time = new Timer(); ...

При вызове функции через поток кнопкой все проходит корректно, но через таймер срабатывает только раз
Приложение для архивирования баз 1С 7.7. все работает хорошо только вот функция ведет себя странно.. При вызове функции через поток...

Таймер срабатывает только 1 раз
Как сделать, если время таймера закончилось, и он выполнил то что ему было задано, то он перезапускался, и делал это же ещё раз, чтобы он...

8
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
15.03.2017, 20:52
Цитата Сообщение от RastaKot Посмотреть сообщение
Во всех туториалах пишут, что надо использовать Windows.Forms.Timer, но в таком случае, падает точность, так как всё выполняется в одном процессе и таймер тикает медленнее, чем должен.
Таймер никогда вам не будет гарантировать точное срабатывание, в каких бы потоках вы его не вызывали.
Нужно делать так:
1) Таймер должен тикать примерно раз в 10 чаще чем те интервалы, которые вам нужны.
2) Нужно создать поле типа DateTime, которое будет хранить время следующего срабатывания. В тике таймера нужно проверять наступило ли время для срабатывания.
3) Если время срабатывания наступило - выполнить нужное действие и сгенерировать следующее время срабатывания (добавить к времени срабатывания константу - например 10 секунд).
4) Если действие длиннее чем интервал времени - то вынесите выполнение в отдельный поток (то есть в тике таймера создавайте и запускайте новый поток).
1
907 / 664 / 318
Регистрация: 23.10.2016
Сообщений: 1,543
15.03.2017, 21:11
Цитата Сообщение от Storm23 Посмотреть сообщение
1) Таймер должен тикать примерно раз в 10 чаще чем те интервалы, которые вам нужны.
Зачем? Вот если бы мы остаток времени до следующего тика ждали при помощи while, тогда да. А так, не понимаю зачем это.
Цитата Сообщение от Storm23 Посмотреть сообщение
Нужно создать поле типа DateTime
Если речь идёт о точности, то лучше использовать поле типа TimeSpan и StopWatch (в комбинации с активным ожиданием).
И вот, кстати, таблица с особенностями таймеров
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
15.03.2017, 21:31
Цитата Сообщение от TopLayer Посмотреть сообщение
Зачем? Вот если бы мы остаток времени до следующего тика ждали при помощи while, тогда да. А так, не понимаю зачем это.
Для того, что бы не накапливалась ошибка. Например, вам нужно что-то делать каждую секунду. Но таймер не будет срабатывать ровно через 1 секунду. Например он будет срабатывать через 1.01 секунду. В результате через 100 секунд вы уже потеряете один тик и будет 99 срабатываний вместо 100.
Для того, что бы избавиться от этого принципиальной погрешности - нужно не опираться на тики, а вести собственный счетчик времени. Ну а таймер должен срабатывать достаточно часто, что бы обеспечить точность времени срабатывания. (знаю, объясняю коряво, но на пальцах это тяжело объяснить )
Цитата Сообщение от TopLayer Посмотреть сообщение
Если речь идёт о точности, то лучше использовать поле типа TimeSpan и StopWatch (в комбинации с активным ожиданием).
TimeSpan и StopWatch это интервал времени, а нам нужно абсолютное время. Если использовать относительные интервалы - будет та же проблема- накопление ошибки.
Кроме того, точность представления не важна, поскольку сам таймер имеет погрешность аж 20 мс.
1
907 / 664 / 318
Регистрация: 23.10.2016
Сообщений: 1,543
15.03.2017, 21:40
Цитата Сообщение от Storm23 Посмотреть сообщение
Для того, что бы не накапливалась ошибка.
Для этого можно просто корректировать время следующего срабатывания при очередном тике.
Впрочем, вот, написал супер-таймер
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class SuperTimer
{
    private TimeSpan _interval;
    private TimeSpan _nextTick;
    private Stopwatch _sw;
    
    public SuperTimer(TimeSpan interval)
    {
        _interval = interval;
    }
    
    public void Start()
    {
        _sw = Stopwatch.StartNew();
        _nextTick = _interval;
        
        Task.Run(async () =>
        {   
            while (true)
            {
                var elapsed = _sw.Elapsed;
                var activeWaitTime = TimeSpan.FromMilliseconds(10);
                var needWait = (_nextTick > elapsed + activeWaitTime) ? _nextTick - activeWaitTime - elapsed : TimeSpan.Zero;
 
                await Task.Delay(needWait);
 
                while (_sw.Elapsed < _nextTick) ;
 
                Tick?.Invoke();
 
                _nextTick += _interval;
            }
        });
    }
    
    public event Action Tick;
}
 
void Main()
{
    var timer = new SuperTimer(TimeSpan.FromMilliseconds(1000));
    var sw = Stopwatch.StartNew();
    timer.Tick += () =>
    {
        Console.WriteLine(sw.Elapsed);
    };
    timer.Start();
}

Вывод:
Кликните здесь для просмотра всего текста
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
00:00:01.0006525
00:00:02.0024050
00:00:03.0004597
00:00:04.0014445
00:00:05.0014933
00:00:06.0004597
00:00:07.0004597
00:00:08.0004597
00:00:09.0004597
00:00:10.0004597
00:00:11.0004597
00:00:12.0004597
00:00:13.0004597
00:00:14.0008642
00:00:15.0004597
00:00:16.0004597
00:00:17.0004597
00:00:18.0004597
00:00:19.0004597
00:00:20.0004597
00:00:21.0004597
00:00:22.0004597
00:00:23.0011995
00:00:24.0004597
00:00:25.0004597
00:00:26.0004597
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
15.03.2017, 21:53
TopLayer, Ну если брать конкретно вашу реализацию, то согласитесь, что она не очень.
Во-первых вы заняли отдельный поток. Если таких таймеров нужно 100 штук, то вы будете держать 100 отдельных потоков... Это плохо. Кстати если покопаться в том же Threading.Timer, то там очень хитро реализовано, и там не занимается отдельный поток под каждый таймер.
Во-вторых вот этот цикл: while (_sw.Elapsed < _nextTick) ; будет просто греть процессор, без всякой полезной нагрузки.
Ну и есть еще много тонких моментов. Например то, что у вас следующий тик не наступит, если не завершился предыдущий тик. А еще ваш таймер нельзя остановить или приостановить.
А еще вот представьте ситуацию - у вас есть сервис который делает бекапы. Делать он должен каждые 24 часа. Допустим вы запустили свой супертаймер и поставили время 24 часа. Но в течении этих 24 часов компьютер мог много раз перезагружаться и ваш интервал - не будет иметь смысла после перезапуска программы. С абсолютным временем таких проблем нет.
2
907 / 664 / 318
Регистрация: 23.10.2016
Сообщений: 1,543
15.03.2017, 22:11
Цитата Сообщение от Storm23 Посмотреть сообщение
Если таких таймеров нужно 100 штук, то вы будете держать 100 отдельных потоков...
Если сто таймеров тикнут примерно в одно время, то куда деваться? А так, у меня на основной части ожидания поток не удерживается.
Запустил 200 таймеров с интервалом 1 сек. Показывает +20 потоков (где-то 11 потоков и с одним таймером). Вот если await Task.Delay заменить на Thread.Sleep, тогда да, +200 потоков.
Цитата Сообщение от Storm23 Посмотреть сообщение
будет просто греть процессор, без всякой полезной нагрузки
Ну это ж ради супер точности.
Цитата Сообщение от Storm23 Посмотреть сообщение
ваш интервал - не будет иметь смысла после перезапуска программы
Но и абсолютное значение тоже надо сохранять не в ОЗУ тогда. Собственно, и в моём подходе можно при сохранении время следующего тика перевести в абсолютное время.
1
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
15.03.2017, 22:18
Цитата Сообщение от TopLayer Посмотреть сообщение
Собственно, и в моём подходе можно при сохранении время следующего тика перевести в абсолютное время.
Так не легче ли просто сразу хранить в абсолютном времени? Зачем усложнять и впихивать невпихуемое?
И кстати вы просто переложили проблему с одних плеч на другие. Потому что методу Task.Delay тоже нужно как-то проверять, наступило ли нужное время. Как он это проверяет? Наверняка тем же самым таймером, и наверняка тики там с максимальной частотой. То есть внутренняя реализация Delay все равно будет такой как я предлагал в посте #2.
1
907 / 664 / 318
Регистрация: 23.10.2016
Сообщений: 1,543
15.03.2017, 22:33
Цитата Сообщение от Storm23 Посмотреть сообщение
То есть внутренняя реализация Delay все равно будет такой как я предлагал в посте #2
Планируется один-единственный тик при помощи System.Threading.Timer
Цитата Сообщение от Storm23 Посмотреть сообщение
Зачем усложнять и впихивать невпихуемое?
Ради точности срабатывания. У DateTime же погрешность равна разрешению таймера: обычно 15мс.
MSDNThe Now property is frequently used to measure performance. However, because of its low resolution, it is not suitable for use as a benchmarking tool. A better alternative is to use the Stopwatch class.

По-прежнему не вижу смысла в десятикратном тиканьи. Нужна точность - грей процессор. Нужно соблюдение среднего времени интервала - корректируй время следующего тика на каждом предыдущем. А 10 лишних тиков - ни к чему.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
15.03.2017, 22:33
Помогаю со студенческими работами здесь

Таймер срабатывает всего один раз
Блок должен сдвигаться на 10px вниз каждую секунду. Всё срабатывает один раз и замирает. Что необходимо добавить ? time (); function...

Таймер срабатывает раньше времени или вообще не срабатывает
Помогите, пожалуйста, разобраться, что нетак с таймером. Браузер Chrome При создании записи, создаётся заметка со следующими...

Как запускать таймер определенное количество раз?
Здрасьте, помогите пожалуйста, хочу сделать чтобы одна процедура запускалась несколько раз(количество задаем в edit), а потом следующая...

Почему деструктор срабатывает больше раз, чем количество созданных объектов?
Вопрос в заголовке темы. Почему? Создается всего 2 объекта, но деструктор вызывается трижды. Вот пример #include &lt;iostream&gt; ...

Не срабатывает таймер в QT
1.Пытаюсь привязать слот к таймеру QTimer timer; QObject::connect(&amp;timer, SIGNAL(timeout()), this, SLOT(sl2())); ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
SDL3 для Web (WebAssembly): Сборка библиотек SDL3 и Box2D из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия SDL 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual. . .
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru