我必须在windows服务中每n分钟运行一次函数。完成后,重新开始。如果功能已启动,则在完成之前无法再次启动。最初我有这样的想法:
void function()
{
while(true)
{
if(!is_running)
{
function_to_be_repeated();
is_running = false;
}
else
{
Thread.Sleep(some_time); // wait to start again
}
}
}
然后我发现了这个http://www.albahari.com/threading/part3.aspx:
bool running = false;
static void Main()
{
// First interval = 5000ms; subsequent intervals = 1000ms
Timer tmr = new Timer (function_to_be_repeated, "...", 5000, 1000);
Console.ReadLine();
tmr.Dispose(); // This both stops the timer and cleans up.
}
static void function_to_be_repeated(object data)
{
if(!running)
{
running = true;
// do some stuff...
running = false;
}
}
但当我按下Enter 时它就停止了
有没有其他方法可以更好地实施它?
我不完全确定问题是什么,因为你提到了一个windows服务和一个结束应用程序的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();
上面的代码可能在控制台应用程序中运行,这就是为什么在按下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();
}
我不确定你遇到的问题是什么,但这里有一个使用计时器的例子,它实际上可以保护自己不与自己并行运行(默认情况下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"中更改代码。