我想在一个任务内部的间隔上运行一个函数。像,
Task t = Task.Factory.StartNew(() => {
while (notCanceled()) {
doSomething();
Thread.Sleep(interval);
}
});
在这里使用Thread.Sleep()是一个坏主意吗?任务是长时间运行的,睡眠时间也可能很长(几分钟,甚至几小时)。
一种替代方法是使用System.Timers.Timer或System.Threading.Timer。但是这两个都会导致产生额外的线程(Elapsed事件发生在新的线程池线程上)。因此,对于每个重复任务,将有2个线程而不是1.任务已经是异步的,所以我不希望以这种方式使事情复杂化。
另一种行为相似的方式是使用ManualResetEvent,
ManualResetEvent m = new ManualResetEvent(false);
void sleep(int milliseconds)
{
m.WaitOne(milliseconds);
}
由于永远不会调用m.Set(),因此它总是等待适当的时间,也是单线程的。这是否比Thread.Sleep()?
有任何明显的优势想知道这里的最佳做法是什么。
思考?
答案 0 :(得分:5)
如果您使用的是C#5.0,则可以使用:
while(notCanceled())
{
doSomething();
await Task.Delay(interval);
}
如果您使用的是早期版本,最好的选择可能是使用Timer
。
您展示的两个代码示例(涉及Thread.Sleep
或ManualResetEvent
)都阻塞了当前线程的持续时间,这意味着您的代码正在占用一个无法执行任何其他操作的线程直到你的任务被取消。你不想那样做。如果您使用计时器或上面提到的await
代码,那么在等待时,根本不会阻塞任何线程,然后只有在您具有生产力时才会耗尽线程池的时间工作要做。
答案 1 :(得分:3)
是的,在这样的循环中使用sleep是一个非常糟糕的主意。
你对计时器的理解有误。创建计时器不会创建新线程。计时器设置一个操作系统触发器,当时间过去时,产生一个线程池线程。所以,如果你写:
System.Threading.Timer myTimer =
new Timer(DoStuff, null,
TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
另一个线程的唯一时间是处理程序方法(DoStuff
)正在执行。
如果您执行的所有操作都由DoStuff
方法处理,则没有理由执行该任务。如果你想取消它,只需处理计时器。
顺便说一下,我强烈建议您不要使用System.Timers.Timer
。简而言之,它会阻止异常,这会隐藏错误。请参阅Swallowing exceptions is hiding bugs。