为了编写一个MIDI音序器,我需要一个稳定的脉冲调用一个时序例程,该例程具有绝对优先于程序中的任何其他内容,并且优先于计算机中的任何内容。我通过使用TimeSetEvent这样做:
TimeSetEvent (FInterval, 0, TimerUpdate, uInt32 (Self), TIME_PERIODIC);
其中TimerUpdate是一个回调函数,它恢复一个优先级为tpTimeCritical的独立线程,并调用一个例程(FOnTimer)处理所有MIDI事件。
procedure TThreaded_Timer.Execute;
begin
if Assigned (FOnTimer) then
begin
while not Terminated do
begin
FOnTimer (Self);
if not Terminated then Suspend;
end; // while
end; // if
Terminate;
end; // Execute //
虽然这种结构比我之前尝试的一些东西要好得多,但它仍然对某些事件非常敏感。令我惊讶的是,每次显示提示都会出现断断续续的情况。为什么简单的提示会导致时间关键线程中断?当然我可以关掉它,但是哪些令人讨厌的惊喜还在等着我?
答案 0 :(得分:5)
使用专为此目的而设计的多媒体计时器。 Delphi定时器非常糟糕,只能在空闲时间得到关注。基于线程的计时器仅在该线程受到关注时才有用。 MMTimer在内核级别运行,并提供非常重要的回调。我们将它用于硬件排序自动化控制它是如此的好。
这是我的单元,它将MMTimer实现为更易于使用的TTimer。使用“重复”使其成为单次或重复的。
unit UArtMMTimer;
interface
uses
Classes,
SysUtils,
ExtCtrls,
MMSystem;
type
TArtMMTimer = class( TObject )
constructor Create;
destructor Destroy; override;
PRIVATE
FHandle : MMResult;
FRepeat : boolean;
FIntervalMS : integer;
FOnTimer : TNotifyEvent;
FEnabled : boolean;
procedure RemoveEvent;
procedure InstallEvent;
procedure DoOnCallback;
procedure SetEnabled( AState : boolean );
procedure SetIntervalMS( AValue : integer );
PUBLIC
property Enabled : boolean
read FEnabled
write SetEnabled;
property OnTimer : TNotifyEvent
read FOnTimer
write FOnTimer;
property IntervalMS : integer
read FIntervalMS
write SetIntervalMS;
end;
implementation
uses
Windows;
// TArtMMTimer
// --------------------------------------------------------------------
procedure MMTCallBack(uTimerID, uMessage: UINT;
dwUser, dw1, dw2: DWORD) stdcall;
var
Timer : TArtMMTimer;
begin
Timer := TArtMMTimer( dwUser );
Timer.DoOnCallback;
end;
constructor TArtMMTimer.Create;
begin
Inherited Create;
FIntervalMS := 100;
FRepeat := True;
end;
destructor TArtMMTimer.Destroy;
begin
FOnTimer := nil;
RemoveEvent;
Inherited Destroy;
end;
procedure TArtMMTimer.RemoveEvent;
begin
If FHandle <> 0 then
begin
timeKillEvent( FHandle );
FHandle := 0;
end;
end;
procedure TArtMMTimer.InstallEvent;
var
iFlags : integer;
begin
RemoveEvent;
If FRepeat then
iFlags := TIME_PERIODIC Or TIME_CALLBACK_FUNCTION
else
iFlags := TIME_CALLBACK_FUNCTION;
FHandle := timeSetEvent(
FIntervalMS,
0,
@MMTCallBack,
DWord(Self),
iFlags );
end;
procedure TArtMMTimer.SetEnabled( AState : boolean );
begin
If AState <> FEnabled then
begin
FEnabled := AState;
If FEnabled then
InstallEvent
else
RemoveEvent;
end;
end;
procedure TArtMMTimer.DoOnCallback;
var
NowHRCount, WaitHRCount,IntervalHRCount : THRCount;
begin
If Assigned( FOnTimer ) then
FOnTimer( Self );
end;
procedure TArtMMTimer.SetIntervalMS( AValue : integer );
begin
If AValue <> FIntervalMS then
begin
FIntervalMS := AValue;
If Enabled then
begin
Enabled := False;
Enabled := True;
end;
end;
end;
// End TArtMMTimer
// --------------------------------------------------------------------
end.
答案 1 :(得分:1)
多媒体计时器的准确性并不高。Here是一篇解释原因的文章。
不是依赖计时器来唤醒你的线程,为什么不在线程本身内管理你的睡眠和唤醒时间呢?
也许这样的事情(在伪代码中,抱歉我不知道Delphi):
my_critical_thread()
{
while (true) {
time = get_current_time()
do_work();
time = interval - (get_current_time() - time)
if (time > 0)
sleep(time)
}
}
如果线程设置为关键优先级,这应该让你非常接近你的目标间隔,假设你在每次迭代时所做的工作为你的其他线程和系统的其余部分留出时间来做他们的事情。
祝你好运。答案 2 :(得分:1)
将计时器设置为比所需时间稍短的时间(例如,减去10毫秒)。
当计时器发生时,将线程优先级提高到“高于正常值”。
计算等待并以稍短的间隔执行睡眠的剩余时间(例如,减少1 ms)。
现在开始在循环中等待正确的时间。在每个循环中,执行至少一个asm暂停;结束;指示不将核心推向100%使用。
当时间发生时,将线程优先级降低到“正常”。
我认为如果不编写设备驱动程序,这是最好的。
答案 3 :(得分:1)
感谢所有建议。为了测试它们,我开发了small Delphi program以测试建议的算法。测试了四种算法
您可以找到结果here。尽管简单的多媒体计时器显示出最大的可变性,但大多数计时器在正常工作负载下运行正确。循环计时器是最精确的。除火灾和火灾外的所有计时器忘记当工作负载导致工作持续时间超过Interval时遇到问题。最好的表现来自火与火。忘记算法。但是,应该注意在回调中不使用共享资源,因为在工作负载增加时可以多次调用回调。事实上,在当前的实现中,MIDIOut可以同时调用,因此它应该被一个关键部分包围。
当运行其他程序时,计时器显示出更大的可变性。循环计时器仍然表现最佳。
当我将计时器添加到我原来的MIDI音序器时,原始问题仍然存在。提示会像以前一样中断计时器,尽管在您可以下载的测试版本中没有这样做。
Parameters Constancy of Beat Workload
Timer N Interval Resolution WorkLoad Mean s.d. Min Max Mean s.d. Min Max
Simple 226 22 30 1000 22.001 0.001 21.996 22.009 0.093 0.036 0.079 0.302
Threaded 226 22 30 1000 22.001 0.004 21.964 22.031 0.091 0.032 0.079 0.253
Looping 227 22 30 1000 22.000 0.002 21.999 22.025 0.093 0.034 0.079 0.197
Fire & Forget 226 22 30 1000 22.001 0.008 21.964 22.042 0.091 0.031 0.079 0.186
Simple 226 22 15 1000 22.001 0.002 21.989 22.011 0.091 0.031 0.079 0.224
Threaded 226 22 15 1000 22.001 0.003 21.978 22.031 0.091 0.032 0.079 0.185
Looping 227 22 15 1000 22.000 0.001 21.999 22.015 0.092 0.034 0.079 0.209
Fire & Forget 226 22 15 1000 22.001 0.015 21.861 22.146 0.091 0.031 0.079 0.173
Simple 226 22 0 1000 22.001 0.001 21.997 22.005 0.091 0.030 0.079 0.190
Threaded 226 22 0 1000 22.001 0.003 21.979 22.029 0.091 0.031 0.079 0.182
Looping 227 22 0 1000 22.000 0.000 21.999 22.002 0.092 0.034 0.079 0.194
Fire & Forget 226 22 0 1000 22.001 0.026 21.747 22.256 0.090 0.030 0.079 0.180
Simple 226 22 30 10000 22.001 0.002 21.992 22.012 0.801 0.034 0.787 1.001
Threaded 226 22 30 10000 22.001 0.002 21.994 22.008 0.800 0.031 0.787 0.898
Looping 227 22 30 10000 22.000 0.000 21.999 22.000 0.802 0.034 0.787 0.919
Fire & Forget 226 22 30 10000 22.001 0.010 21.952 22.087 0.903 0.230 0.788 1.551
Simple 226 22 15 10000 22.001 0.002 21.984 22.020 0.810 0.081 0.788 1.417
Threaded 226 22 15 10000 22.001 0.006 21.981 22.073 0.800 0.031 0.788 0.889
Looping 227 22 15 10000 22.000 0.000 21.999 22.000 0.802 0.036 0.787 0.969
Fire & Forget 226 22 15 10000 22.001 0.009 21.914 22.055 0.799 0.030 0.788 0.885
Simple 226 22 0 10000 22.001 0.002 21.994 22.006 0.799 0.030 0.788 0.894
Threaded 226 22 0 10000 22.001 0.005 21.953 22.048 0.799 0.030 0.787 0.890
Looping 227 22 0 10000 22.000 0.000 21.999 22.002 0.801 0.034 0.787 0.954
Fire & Forget 226 22 0 10000 22.001 0.007 21.977 22.029 0.799 0.030 0.788 0.891
Simple 226 22 30 100000 22.001 0.002 21.988 22.017 7.900 0.052 7.879 8.289
Threaded 226 22 30 100000 22.001 0.003 21.967 22.035 7.897 0.036 7.879 8.185
Looping 227 22 30 100000 22.000 0.001 21.999 22.015 7.908 0.098 7.879 9.165
Fire & Forget 225 22 30 100000 22.001 0.007 21.960 22.027 7.901 0.038 7.880 8.061
Simple 227 22 15 100000 22.014 0.195 21.996 24.934 7.902 0.056 7.879 8.351
Threaded 226 22 15 100000 22.001 0.002 21.997 22.008 7.900 0.049 7.879 8.362
Looping 227 22 15 100000 22.000 0.000 22.000 22.000 7.900 0.046 7.879 8.229
Fire & Forget 225 22 15 100000 22.001 0.008 21.962 22.065 7.906 0.082 7.880 8.891
Simple 227 22 0 100000 22.018 0.261 21.937 25.936 7.901 0.050 7.879 8.239
Threaded 226 22 0 100000 22.001 0.001 21.998 22.005 7.897 0.031 7.879 7.987
Looping 227 22 0 100000 22.000 0.000 21.999 22.000 7.901 0.053 7.879 8.263
Fire & Forget 225 22 0 100000 22.001 0.007 21.967 22.032 7.900 0.044 7.880 8.308
Simple 63 22 30 1000000 78.027 6.801 24.938 80.730 77.754 8.947 7.890 80.726
Threaded 56 22 30 1000000 87.908 1.334 78.832 91.787 78.897 0.219 78.819 80.430
Looping 62 22 30 1000000 78.923 0.320 78.808 80.749 78.923 0.320 78.808 80.748
Fire & Forget 222 22 30 1000000 22.001 0.009 21.956 22.038 84.212 3.431 78.825 91.812
Simple 66 22 15 1000000 75.656 13.090 21.994 80.714 79.183 1.559 78.811 90.950
Threaded 56 22 15 1000000 87.841 1.204 78.991 88.011 78.849 0.043 78.812 79.003
Looping 62 22 15 1000000 78.880 0.207 78.807 80.442 78.880 0.207 78.807 80.441
Fire & Forget 222 22 15 1000000 22.001 0.978 11.975 32.042 84.915 3.569 78.816 90.917
Simple 66 22 0 1000000 75.681 12.992 21.991 80.778 79.213 1.400 78.807 87.766
Threaded 56 22 0 1000000 87.868 1.238 78.889 89.515 78.954 0.597 78.813 83.164
Looping 62 22 0 1000000 78.942 0.307 78.806 80.380 78.942 0.307 78.806 80.379
Fire & Forget 222 22 0 1000000 22.001 0.011 21.926 22.076 83.953 3.103 78.821 91.145