如何将此时间关键程序优先于其他线程?

时间:2011-09-24 15:28:01

标签: multithreading delphi

为了编写一个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 //

虽然这种结构比我之前尝试的一些东西要好得多,但它仍然对某些事件非常敏感。令我惊讶的是,每次显示提示都会出现断断续续的情况。为什么简单的提示会导致时间关键线程中断?当然我可以关掉它,但是哪些令人讨厌的惊喜还在等着我?

4 个答案:

答案 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以测试建议的算法。测试了四种算法

  • 简单(由Brian Frost提供) - 使用多媒体计时器(TimeSetEvent),它调用执行定时任务的回调。
  • Threaded(作者:Brian Pedersen) - 与简单一样,但回调是在一个单独的线程中调用的。线程可能获得最高优先级(TimeCritical)。
  • 循环(由Miguel提供) - 在这个算法中,我们根本不信任计时器,而是自己写一个。回调是在一个循环中执行的,在每次调用之后我们会检查在下一个滴答之前还剩下多少时间并等到下一个必须发生。该线程具有最高优先级。我用asm暂停;结束Gabr的建议,作为一种快速的事件处理方式。
  • Fire&amp;忘记(由我自己) - 每个tick的多媒体计时器创建一个具有最高优先级的单独线程,为其分配回调并忘记它。这样做的好处是,即使前一个线程尚未完成,一个新的线程也可以启动,如果幸运的话 - 在一个新的处理器上。

您可以找到结果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