2013-05-15 29 views
2

我想在任务内部的时间间隔上运行函数。类似的,单线程计时器和/或任务内的时间间隔<T>

 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

你对Timer有错误的想法。它使用*一个* tp线程,一个短跑。你不再使用现在使用的昂贵的长时间运行的。 –

回答

5

如果您正在使用C#5.0,你可以使用:

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

如果您使用的是较早版本你最好的选择可能是使用一个Timer

代码样本你们俩显示,无论是参与还是Thread.Sleep一个ManualResetEvent被阻止在该持续时间,这意味着你的代码是占用一个线程,直到你的任务被取消,不能做别的当前线程。你不想这样做。如果你使用一个定时器,或者上面提到的await代码,你将在等待的时候终止不会阻塞任何线程,然后只有当你有生产力的工作要做时才用完线程池的时间。

+0

那么你是否建议我在任务中使用计时器,或者根本就不使用任务?我已经广泛地使用TPL,这将是一个更大的设计变更。如果我在任务中使用了一个定时器,那么当定时器重复该功能时,我仍然必须保持任务存活,否则在定时器仍在尝试执行时,不会在任务上调用ContinueWith()函数工作。由于定时器的Start()方法是非阻塞的,我认为这个任务会立即完成。 –

+1

@SeanThoman无论你是否在任务内创建计时器都没关系;你所做的只是启动定时器,而不是执行一堆代码。如果你想要一个代表上次定时器运行的任务,你根本不需要调用'StartNew';因为定时器不代表单个方法的执行。使用'TaskCompletionSource'来生成一个任务,当你完成所有定时器的迭代时,你可以标记为已完成。如果您使用首先显示的C#5.0方法,那是为您有效完成的。 – Servy

+0

谢谢。 TaskCompletionSource <>运行良好。 –

3

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

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

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

唯一一次会有时处理方法(DoStuff)正在执行另一个线程的。

如果所做的一切都由您的DoStuff方法处理,那么没有理由完成任务。如果你想取消它,只需处理定时器。

我强烈建议,顺便说一句,你不使用System.Timers.Timer。简而言之,它会压缩异常,从而隐藏错误。见Swallowing exceptions is hiding bugs

+0

我使用TPL提供的延续和故障处理。如果定时器的Start()方法(或构造函数)是非阻塞的,那么该任务立即完成。使用TaskCompletionSource <>也许是正确的。 –

+0

+1“吞咽”。题外话:我读过.NET中任何计时器(Windows)永远都是异步的短语 –