C# 在c中实现计时器的另一种方法#
在windows服务中,我必须每n分钟运行一次函数。完成后,重新开始。如果函数已启动,则在完成之前无法再次启动。最初我有一个想法:C# 在c中实现计时器的另一种方法#,c#,windows,windows-services,C#,Windows,Windows Services,在windows服务中,我必须每n分钟运行一次函数。完成后,重新开始。如果函数已启动,则在完成之前无法再次启动。最初我有一个想法: void function() { while(true) { if(!is_running) { function_to_be_repeated(); is_running = false; } else { Thread.Sleep(some_time); // wait to
void function()
{
while(true)
{
if(!is_running)
{
function_to_be_repeated();
is_running = false;
}
else
{
Thread.Sleep(some_time); // wait to start again
}
}
}
然后我发现:
但当我按下回车键时它就停止了
还有其他更好的实现方法吗?上面的代码可能是在控制台应用程序中运行的,这就是为什么按ENTER键后会出现它(更准确地说,这是因为
console.Readline()
)
但是,您还提到在Windows服务中需要此功能
在Windows服务中,您通常会覆盖ServiceBase
的OnStart()
和OnStop()
方法。在您的情况下,服务代码如下所示:
private Timer tmr;
protected override void OnStart(string[] args)
{
tmr = new Timer (function_to_be_repeated, "...", 5000, 1000);
}
protected override void OnStop()
{
tmr.Dispose();
}
我不完全确定问题是什么,因为你提到了windows服务和结束应用程序的Enter键,然后你提到了更好的实现 首先,如果您在控制台中运行它,并且不希望enter键杀死它,您可以执行以下操作
do
{
}
while (!Console.ReadLine().Equals("exit",
StringComparison.InvariantCultureIgnoreCase)
);
而不是
Console.ReadLine();
这意味着您的控制台只有在您键入exit并按enter,或者您通过按x按钮或终止进程来关闭程序时才会结束
至于更好的实现,我不确定这是否是最佳实践,但我会这样做,使用轮询线程实现:
class Poll : IDisposable
{
private TimeSpan polledSpan;
WaitHandle[] handles = new WaitHandle[2];
ManualResetEvent exit = new ManualResetEvent(false);
Thread thread;
public Poll(int polledTime)
{
polledSpan = new TimeSpan(0, 0, polledTime);
thread = new Thread(new ThreadStart(Start));
thread.Start();
}
private void Start()
{
AutoResetEvent reset = new AutoResetEvent(false);
handles[0] = reset;
handles[1] = exit;
bool run = true;
while (run)
{
int result = WaitHandle.WaitAny(handles,
(int)polledSpan.TotalMilliseconds,
false);
switch(result)
{
case WaitHandle.WaitTimeout:
run = StuffToDo();
break;
case 1:
case 0:
run = false;
break;
}
}
}
private bool StuffToDo()
{
try
{
Console.WriteLine("Test");
return true;
}
catch
{
return false;
}
}
public void Dispose()
{
exit.Set();
if (thread != null)
{
thread.Join(10000);
}
exit = null;
handles = null;
}
}
主要方法是
Poll p = new Poll(1);
do
{
}
while (!Console.ReadLine().Equals("exit",
StringComparison.InvariantCultureIgnoreCase));
p.Dispose();
我不确定您遇到的问题是什么,但下面是一个使用计时器的示例,它实际上可以保护自己不与自身并行运行(默认情况下System.Threading.timer不会这样做) 另外,如果您想在windows服务中运行它,只需将计时器的创建移动到start方法,并在属性中停止并保留计时器实例
class Program
{
static void Main(string[] args)
{
var timeBetweenTicks = (long) TimeSpan.FromSeconds(1).TotalMilliseconds;
System.Threading.Timer timer = null;
Action onTick = () =>
{
timer.Change(Timeout.Infinite, Timeout.Infinite);
try
{
//Work on tick goes here
Console.WriteLine(DateTime.Now);
}
finally
{
timer.Change(timeBetweenTicks, timeBetweenTicks);
}
};
using (timer = new System.Threading.Timer(_ => onTick(), null, 0, timeBetweenTicks))
{
Console.ReadKey();
}
}
}
请注意,如果您希望下一个滴答声的时间受当前滴答声所用时间的影响,则需要添加一些计时,并在“finally”(最终)中更改代码。“但当我按Enter键时它会停止”-是的,代码就是这样做的。现在你有问题了吗?我倾向于使用
AutoResetEvent
计时器,而不是使用Thread.Sleep
,它工作得相当好。我不能说它是否是建议的解决方案,但它为我提供了一个更轻松的解决方案。在.NET中有许多不同的类,名为Timer
(在不同的名称空间中)。你用哪一种?
class Program
{
static void Main(string[] args)
{
var timeBetweenTicks = (long) TimeSpan.FromSeconds(1).TotalMilliseconds;
System.Threading.Timer timer = null;
Action onTick = () =>
{
timer.Change(Timeout.Infinite, Timeout.Infinite);
try
{
//Work on tick goes here
Console.WriteLine(DateTime.Now);
}
finally
{
timer.Change(timeBetweenTicks, timeBetweenTicks);
}
};
using (timer = new System.Threading.Timer(_ => onTick(), null, 0, timeBetweenTicks))
{
Console.ReadKey();
}
}
}