2

我想在任务内部的间隔上运行一个函数。就像是,

       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() 相比有什么显着优势吗?

想知道这里的最佳做法是什么。

想法?

4

2 回答 2

5

如果您使用的是 C# 5.0,则可以使用:

while(notCanceled())
{
    doSomething();
    await Task.Delay(interval);
}

如果您使用的是早期版本,最好的选择可能是使用Timer.

您展示的两个代码示例(涉及其中一个Thread.Sleep或一个ManualResetEvent)都在该持续时间内阻塞了当前线程,这意味着您的代码正在占用一个线程,该线程在您的任务被取消之前无法执行任何其他操作。你不想那样做。如果您使用计时器或await上面提到的代码,您最终不会在等待时阻塞任何线程,然后只有在您有生产性工作要做时才用完线程池的时间。

于 2013-05-15T19:35:19.357 回答
3

是的,在这样的循环中使用睡眠是一个非常糟糕的主意。

你对定时器有错误的理解。创建计时器不会创建新线程。计时器设置一个操作系统触发器,当时间过去时,它会产生一个线程池线程。所以如果你写:

System.Threading.Timer myTimer = 
    new Timer(DoStuff, null, 
              TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));

DoStuff只有在处理程序方法 ( ) 正在执行时才会有另一个线程。

如果它所做的一切都由您的DoStuff方法处理,则没有理由拥有该任务。如果要取消它,只需处置计时器。

顺便说一句,我强烈建议您不要使用System.Timers.Timer. 简而言之,它压制了隐藏错误的异常。请参阅吞咽异常隐藏错误

于 2013-05-15T20:24:44.043 回答