Как заменить sleep в потоке? чтобы получить задержку 5 мили
От: Dasverd  
Дата: 01.09.10 08:13
Оценка:
Здравствуйте Уважаемые!
Мне нужна Ваша помощь.

Есть поток, мне нужно задержать процедуру (Updatedann) на 5 милисек.


procedure TDRThread.Execute;

begin
 index:=1;

 //Запускаю бесконечный цикл
 while index>0 do
    begin
    Synchronize(Updatedann);
    sleep(5);
   
    if terminated then exit; //Если поток остановлен, то выйти,
    end;
end;




А на самом деле процедура (Updatedann) выполняется только через 15 мсек.

Если вообще убрать sleep, то процедура (Updatedann) выполняется за 0.16 милисек ~(примерно, очень быстро).



Как сделать что бы процедура выполнялась через каждые 5 милисек?
Re: Как заменить sleep в потоке? чтобы получить задержку 5 м
От: Rius Россия  
Дата: 01.09.10 09:21
Оценка:
void __fastcall delay(DWORD microseconds)
{
    LARGE_INTEGER pc1;
    LARGE_INTEGER pc0;
    LARGE_INTEGER pf;
    LONGLONG ticks;
    if(QueryPerformanceFrequency(&pf))
    {
        ticks  = pf.QuadPart * microseconds / 1000000;
        QueryPerformanceCounter(&pc0);
        do
        {
            QueryPerformanceCounter(&pc1);
        } while (pc1.QuadPart - pc0.QuadPart < ticks);
    }
}
Re[2]: Как заменить sleep в потоке? чтобы получить задержку
От: Dasverd  
Дата: 01.09.10 12:55
Оценка:
Здравствуйте, Rius, Вы писали:

R>
void __fastcall delay(DWORD microseconds)
R>{
R>    LARGE_INTEGER pc1;
R>    LARGE_INTEGER pc0;
R>    LARGE_INTEGER pf;
R>    LONGLONG ticks;
R>    if(QueryPerformanceFrequency(&pf))
R>    {
R>        ticks  = pf.QuadPart * microseconds / 1000000;
R>        QueryPerformanceCounter(&pc0);
R>        do
R>        {
R>            QueryPerformanceCounter(&pc1);
R>        } while (pc1.QuadPart - pc0.QuadPart < ticks);
R>    }
R>}


Rius, спасибо огромное за ответ, только я не могу понять, можешь подробнее описать, половина операторов не знаю, к моиму примеру можешь написать? Буду очень благодарен.
Re[3]: Как заменить sleep в потоке? чтобы получить задержку
От: Dasverd  
Дата: 01.09.10 13:07
Оценка:
Здравствуйте, Dasverd, Вы писали:

D>Здравствуйте, Rius, Вы писали:


R>>
void __fastcall delay(DWORD microseconds)
R>>{
R>>    LARGE_INTEGER pc1;
R>>    LARGE_INTEGER pc0;
R>>    LARGE_INTEGER pf;
R>>    LONGLONG ticks;
R>>    if(QueryPerformanceFrequency(&pf))
R>>    {
R>>        ticks  = pf.QuadPart * microseconds / 1000000;
R>>        QueryPerformanceCounter(&pc0);
R>>        do
R>>        {
R>>            QueryPerformanceCounter(&pc1);
R>>        } while (pc1.QuadPart - pc0.QuadPart < ticks);
R>>    }
R>>}


D>Rius, спасибо огромное за ответ, только я не могу понять, можешь подробнее описать, половина операторов не знаю, к моиму примеру можешь написать? Буду очень благодарен. Я бы хотел ответ на делфи...
Re[4]: Как заменить sleep в потоке? чтобы получить задержку
От: Sapersky  
Дата: 01.09.10 13:57
Оценка:
Здравствуйте, Dasverd, Вы писали:

D>>Rius, спасибо огромное за ответ, только я не могу понять, можешь подробнее описать, половина операторов не знаю, к моиму примеру можешь написать? Буду очень благодарен. Я бы хотел ответ на делфи...


1) Этот метод, хотя и даст более-менее точную задержку, но загрузит процессор (по крайней мере одно ядро) на 100%. Зачем вам задержка в 5 мс, чем 15 не устраивает?
2) Поток, который вызывает в цикле Synchronize, не имеет смысла, т.к. Synchronize вызывается в контексте главного потока.
Re[4]: Как заменить sleep в потоке? чтобы получить задержку
От: Rius Россия  
Дата: 01.09.10 17:48
Оценка:
Здравствуйте, Dasverd, Вы писали:

D>>Rius, спасибо огромное за ответ, только я не могу понять, можешь подробнее описать, половина операторов не знаю, к моиму примеру можешь написать? Буду очень благодарен. Я бы хотел ответ на делфи...


QueryPerformanceFrequency и QueryPerformanceCounter это функции из WinAPI.
Вообще действительно, при использовании Synchronize всё это бессмысленно.
Re: Как заменить sleep в потоке? чтобы получить задержку 5 м
От: Mr.Delphist  
Дата: 02.09.10 07:28
Оценка:
Здравствуйте, Dasverd, Вы писали:

D>Как сделать что бы процедура выполнялась через каждые 5 милисек?


А что ты будешь делать, если тебя посередине этого кода планировщик потоков остановит? И уж делай слипы/спинлоки/etc или не делай, но управление обратно получишь через столько времени, через сколько дадут. Может, 15 мс, а может и 15 часов

Опиши, зачем такой жесткий интервал и чем грозит его нарушение? Ибо система Windows не является real-time OS, поэтому гарантий о временных интервалах тебе никто не даст.
Re: Как заменить sleep в потоке? чтобы получить задержку 5 м
От: Pavel Dvorkin Россия  
Дата: 04.09.10 04:04
Оценка:
Здравствуйте, Dasverd, Вы писали:

D>Есть поток, мне нужно задержать процедуру (Updatedann) на 5 милисек.


D>Как сделать что бы процедура выполнялась через каждые 5 милисек?


Корректно и надежно — никак. By design Windows.
With best regards
Pavel Dvorkin
Re: Как заменить sleep в потоке? чтобы получить задержку 5 м
От: Jolly Roger  
Дата: 05.09.10 08:02
Оценка:
Здравствуйте, Dasverd, Вы писали:

D>Как сделать что бы процедура выполнялась через каждые 5 милисек?


Вы можете получить близкое к желаемому поведение двумя способами.
1) Вместо потока использовать таймер, создаваемый функцией timeSetEvent. Разрешение до 1-й миллисекунды, а коллбэк вызывается в отдельном потоке.
2) Вызвав в начале своего потока timeBeginPeriod(1), а вконце — timeEndPeriod(1)

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

Да, на всякий случай — не пользуйтесь для контроля функцией GetTickCount, этот счётчик слишком редко обновляется. Ориентируйтесь на QueryPerformanceCounter
"Нормальные герои всегда идут в обход!"
Re: Как заменить sleep в потоке? чтобы получить задержку 5 м
От: Dasverd  
Дата: 07.09.10 09:30
Оценка:
Всем огромное СПАСИБО!!! Проблема решена.

Парни, sleep() производит задержку 5 милисекунд, и 1 милисекунду, вот только я, не правильно замерял.

Я замерял время задержки (sleep), функцией:

     var
     NewTim, OldTim, VipTim      :TDateTime;
     Hour, Min, Sec, MSec         :Word;

     Begin
      NewTim:=Now;

       // задержка 15 милисекунд
      sleep(15);  
      //(или фукция которую Вы хотите измерить)

      OldTim:=Now;
      VipTim:=OldTim-NewTim;
      DecodeTime(VipTim, Hour, Min, Sec, MSec); 
      
      //перевожу в милисекунды 
      VipTim:=Hour;
      VipTim:=VipTim*3600;
      VipTim:=VipTim+Min*60;
      VipTim:=VipTim+Sec;
      VipTim:=VipTim*1000+MSec;
      
      ShowMessage('Время выполнения фукции = '+floattostr(VipTim)+' милисек');
     End;


Оказывается таким способом можно считать время выполнения функции, но если функция выполняется быстрее 15 милисекунд, то бесмыссленно.

Вот результаты:

при sleep(15);
Будет выводится сообщение: (Время выполнения фукции = ~ 15 милисек)

при sleep(5);
Будет выводится сообщение: (Время выполнения фукции = ~15 милисек)


Более точные замеры можно производить следующем способом:

С помощью функций ( QueryPerformanceFrequency, QueryPerformanceCounter)

QueryPerformanceCounter — функция Win32 API.
QueryPerformanceFrequency — возвращает частоту счётчика count/sec.
QueryPerformanceCounter — возвращает текущее значение счетчика с высоким разрешением производительности.

  var
 Ctr1, Ctr2, Freq,Overhead: int64; 
 R: extended; 

  Begin
   QueryPerformanceFrequency(Freq); 
   QueryPerformanceCounter(Ctr1); 
   QueryPerformanceCounter(Ctr2); 
   Overhead := Ctr2 - Ctr1; 
   QueryPerformanceCounter(Ctr1); 

   //здесь Ваша функция или процедура которую Вы хотите измерить
   // допустим задержка sleep(5);

   sleep(5);

   QueryPerformanceCounter(Ctr2); 
   R := ((Ctr2 - Ctr1) - Overhead) / Freq; 
   showmessage( 'Ваша функция выполнилась за ' + FloatToStr(R) + '  секунд');


Результат: Ваша функция выполнилась за 0,0049975 секунд



Следовательно, функция sleep(1) производит задержку в 1 милисекунду, только измерять нужно функциями (QueryPerformanceFrequency, QueryPerformanceCounter).

Так же я нашел способ задержки функции в микросекундах:

procedure DelayUS(MicroS:int64); // Ожидание N microSec
var
  Frq_Base, Time_memo, Time_now, dif: Int64;
begin
  if QueryPerformanceFrequency(Frq_Base) then // Частота ПК
    begin
      QueryPerformanceCounter(Time_memo);        // начальное значение
      repeat
      QueryPerformanceCounter(Time_now);
      dif := ((Time_now - Time_memo) * 1000000) div Frq_Base;
      until dif > MicroS;
    end;
end;



Begin

DelayUS(4550); // Задержка микросек

End;



Еще раз спасибо, что посоветовали QueryPerformanceCounter().
Re[2]: Как заменить sleep в потоке? чтобы получить задержку
От: Jolly Roger  
Дата: 08.09.10 05:17
Оценка:
Здравствуйте, Dasverd, Вы писали:



D>Следовательно, функция sleep(1) производит задержку в 1 милисекунду


Значит, у Вас в системе какое-то приложение уже вызвало функцию timeBeginPeriod(1) и не вызвало timeEndPeriod(1). По хорошему за такое надо-бы руки отрывать.
"Нормальные герои всегда идут в обход!"
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.