我必须每隔n分钟在Windows服务中运行一个函数。完成后,重新开始。如果该功能已启动,则在完成之前无法再次启动。最初我有这个想法:
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
时它停止了还有其他方法可以更好地实施吗?
答案 0 :(得分:1)
当你提到一个Windows服务和一个结束你的应用程序的Enter键时,我不完全确定问题是什么,然后你提到了一个更好的实现。
首先,如果您在控制台中运行它并且不想让Enter键杀死它,您可以执行类似
的操作do
{
}
while (!Console.ReadLine().Equals("exit",
StringComparison.InvariantCultureIgnoreCase)
);
而不是
Console.ReadLine();
这意味着您的控制台只会在您输入退出并按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();
答案 1 :(得分:0)
上面的代码可能在控制台应用程序中运行,这就是在你按 ENTER 之后它存在的原因(更准确地说,这是因为Console.Readline()
)。
但是,您还提到在Windows服务中需要此功能。
在Windows服务中,您通常会覆盖OnStart()
的{{1}}和OnStop()
方法。在您的情况下,服务代码将如下所示:
ServiceBase
答案 2 :(得分:0)
我不确定你遇到了什么问题,但这里有一个使用计时器的例子,它实际上可以防止自己与自身并行运行(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”中的代码。