在编写Windows服务时,只需检查是否有最佳做法。
服务(单线程)需要按指定的时间间隔工作,现在我只能想到:
有什么建议吗?
答案 0 :(得分:13)
您的服务是单线程的并不重要,因为服务的代码总是在不同的线程上下文中调用:
服务管理器将启动,停止,暂停和恢复服务执行,并请求当前的服务状态。
服务本身将至少有一个执行实际工作的线程,需要对来自服务管理器的请求做出反应,根据请求更改服务执行状态,并返回所请求的信息。服务需要在合理的短时间内响应来自服务管理器的请求,否则它会认为服务被挂起并将其终止。这就是为什么 - 如果服务可能有长期执行或阻塞代码 - 拥有多个服务线程可能会更好。
是否使用 Sleep()或计时器消息还取决于服务线程中消息泵的可用性。如果您没有消息泵,则应使用 Sleep()或计时器回调。如果你有一个消息泵,因为你需要通过Windows消息与其他进程或线程通信,或者你需要做OLE的东西,那么使用定时器消息可能是最简单的。
几年前,我编写了一个服务,用于定时执行任务,类似于Windows at
或Unix cron
功能。它不使用大部分VCL,只使用一些基类。该服务的 Run()方法如下所示:
procedure TScheduleService.Run;
var
RunState: TServiceRunState;
begin
while TRUE do begin
RunState := GetServiceRunState;
if (RunState = srsStopped) or (fEvent = nil) then
break;
if RunState = srsRunning then begin
PeriodicWork;
Sleep(500);
end else
fEvent.WaitFor(3000);
Lock;
if fServiceRunStateWanted <> srsNone then begin
fServiceRunState := fServiceRunStateWanted;
fServiceRunStateWanted := srsNone;
end;
Unlock;
end;
end;
这在循环中使用 Sleep(),但是使用
的解决方案while integer(GetMessage(Msg, HWND(0), 0, 0)) > 0 do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
也可以正常工作,然后可以使用Windows计时器消息。
答案 1 :(得分:10)
这需要是一项服务吗?你可以在Windows中设置一个计划任务吗?
答案 2 :(得分:3)
我会使用睡眠。
这两个选项都没有确切的时间保证,但是sleep会将资源返回给其他进程。
答案 3 :(得分:3)
TTimer不是线程安全的。如果您必须在线程中使用类似TTimer的方法,我建议DSiWin32使用TDSiTimer。
答案 4 :(得分:2)
永远不要在服务中使用TTimer,它不会总是按照您的预期运行,并且它不是线程安全的。
在服务中,我总是使用自己的时间间隔变量,并在任务执行时间之间休眠。为了使服务保持响应,我通常会短时间睡眠1-2000毫秒,然后在检查我的间隔之前处理消息,以了解是否是执行“任务”的时间。如果还没有时间,请回到睡眠状态,并在循环后再次检查。通过这种方式,您可以返回资源,但也可以在下一个任务执行之前响应用户输入(停止,暂停)。
答案 5 :(得分:0)
我在服务中使用类似的东西:
unit uCopy;
interface
uses
Windows, Messages,.......;
procedure MyTimerProc(hWindow : HWND; uMsg : cardinal; idEvent : cardinal; dwTime : DWORD); stdcall;
type
TFileCopy= class(TService)
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
VAR
timerID : UINT;
const
SECONDS = 900000;
procedure TFileCopy.ServiceStart(Sender: TService;
var Started: Boolean);
Begin
timerID := 0; //Probably not needed.
timerID := SetTimer(0, 1, SECONDS, @MyTimerProc);
End;
procedure MyTimerProc(hWindow : HWND; uMsg : cardinal; idEvent : cardinal;dwTime : DWORD); stdcall;
Begin
//Kill timer while trying.. If this function takes longer than the interval to run, I didn't want a build up. If that was possible.
KillTimer(0, timerID);
timerID := 0; //Is it needed?
//DO WORK.
{
//I was Connecting to a Network Drive, Minutes, seconds.....
//I only wanted to run this Every day at 2 AM.
//So I had my timer set to 15 minutes, once it got between 30 and 10 minutes of my 2 AM deadline,
//i killed the existing timer and started a new one at 60000.
//If it was within 10 minutes of my 2 AM, my interval changed to 500.
//This seems to work for me, my files get copied everyday at 2 AM.
}
//Work Complete. Start timer back up.
timerID := SetTimer(0, 1, SECONDS, @MyTimerProc);
End;
procedure TFileCopy.ServiceStop(Sender: TService;
var Stopped: Boolean);
Begin
if timerID > 0 then
KillTimer(0, timerID);
End;
当然,我在大多数地方都有一些Try..Catch,还有写日志和发送电子邮件.... 我已经使用这些技术运行了一年多的服务。 绝不是规则。 请告诉我是否有更好的方法。 我一直在寻找提高德尔福知识的方法。 另外,如果我错过了发布问题的截止日期,那就很抱歉。
-Trey Aughenbaugh