2013-02-24 66 views
1

我想在不使用Thread.sleep的情况下执行以下操作。基本上,我必须调用3个函数,并在每个函数之间等待一段时间,然后将整个事件循环运行100次。如何在函数之间等待

我尝试使用Timer类,但它似乎我使用不正确。

func test() 
for(int i=0;i<100;i++){ 
     func A() 
     wait for 20 seconds 
     func B() 
     wait for 45 seconds 
     func C() 
     wait for 2 mins 
}//repeat for 100 times 
+5

为什么你不能使用Thread.Sleep?它在等待时释放线程,因此如果需要延迟,它看起来很理想。 – driis 2013-02-24 18:34:31

+0

为什么你不想使用Thread.Sleep?这正是它的设计目的。 – 2013-02-24 18:34:37

+4

@driis:它不会“释放”线程 - 它不像线程可用于其他工作。它不占用大量的CPU,但我不会称之为“释放”它。 – 2013-02-24 18:38:22

回答

0

我要去做出了重要的假设(所有线索都指向那个方向):

我假设你正在做的这一切在Windows窗体和你想的GUI不要在Thread.Sleep()调用期间冻结,对吗?如果是这样的话,那么你可以写这个方法:

public static class Foo { 
    public static void MySleep(int milliseconds) { 
    int startTick = Environment.TickCount; 
    while (Environment.TickCount - startTick < milliseconds) 
     Application.DoEvents(); 
    } 
} 

,然后替换Foo.MySleep调用Thread.sleep代码调用(但当心Application.DoEvents的含义,这使得整个过程当控制位于任何MySleep调用内部时,再次执行。如果您不采取预防措施,我不推荐这样做 - 例如,您应该始终禁用调用的按钮func test( )测试正在执行,因为它可以一次又一次地在永无止境的递归中执行) :

func test() 
    for(int i=0;i<100;i++){ 
    func A() 
    Foo.MySleep(20 * 1000); 
    func B() 
    Foo.MySleep(45 * 1000); 
    func C() 
    Foo.MySleep(2 * 60 * 1000); 
}//repeat for 100 times 

还有另一种方法(更安全一点)做这件事。它涉及一个辅助线程和一些Control.Invoke调用。它也允许你使用标准的System.Thread.Sleep(int milliseconds)方法。

它是这样的:

public class Form1 : Form { 

    public void triggerTestAndReturnImmediately() { 
    new Thread(start:() => { 

     Action callA =() => funcA(); 
     Action callB =() => funcB(); 
     Action callC =() => funcC(); 

     for(int i=0;i<100;i++){ 
     this.Invoke(callA); 
     Thread.Sleep(20 * 1000); 

     this.Invoke(callB); 
     Thread.Sleep(45 * 1000); 

     this.Invoke(callC); 
     Thread.Sleep(2 * 60 * 1000); 
     }//repeat for 100 times 


    }).Start(); 
    } 

} 

的“this.Invoke(someDelegate)”位是基于事实的方法“triggerTestAndReturnImmediately”是形式的子类的成员,“本”是一个Form实例。

这里发生的事情是,您在等待操作+实际工作者(funcA,funcB,funcC)的触发器上运行一个单独的线程。你为什么只运行真正的工人而不是真正的工人呢?因为你很可能会从这些funcs访问GUI对象,而WinForms(以及许多其他.NET或非.NET GUI框架)不允许这样做。

因此,您要求GUI线程(正如您在您的问题中指出的那样)在99%的时间内执行这些功能时必须是免费的 - 因此需要调用this.Invoke(someDelegate)调用。

您应该采取与我前面提到的相同的预防措施,但所有这些都取决于您以及您希望应用程序执行的操作。假设你想让一个按钮的Click事件处理程序调用triggerTestAndReturnId立即,并且希望在此过程中禁用它,那么如何禁用并重新启用该按钮?

如果你这样做:

void buttonBar_Click(object sender, EventArgs e) { 
    this.buttonBar.Enabled = false; 
    this.triggerTestAndReturnImmediately(); 
    this.buttonBar.Enabled = true; 
} 

那么这将不会给你带来任何好,因为triggerTestAndReturnImmediately方法不正是它的名字的含义是:“它立即返回”。

那么你会如何解决这个问题?你可以这样做:

void buttonBar_Click(object sender, EventArgs e) { 
    this.triggerTestAndReturnImmediately(); 
} 


    public void triggerTestAndReturnImmediately() { 
    this.buttonBar.Enabled = false; 

    new Thread(start:() => { 

     Action callA =() => funcA(); 
     Action callB =() => funcB(); 
     Action callC =() => funcC(); 
     Action reenableButton =() => this.buttonBar.Enabled = true; 

     for(int i=0;i<100;i++){ 
     this.Invoke(callA); 
     Thread.Sleep(20 * 1000); 

     this.Invoke(callB); 
     Thread.Sleep(45 * 1000); 

     this.Invoke(callC); 
     Thread.Sleep(2 * 60 * 1000); 

     this.Invoke(reenableButton); 
     }//repeat for 100 times 


    }).Start(); 
    } 

另外,还要确保你总是重新启用按钮调用序列是否崩溃或无法与某些尝试终于声明或其他体操。

所以我认为你可以了解我所谈论的预防措施是什么(它们取决于你想要你的应用程序做什么)。

2

您可能正在寻找任务框架。你可以这样做:

async void Run() 
{ 
    for (int i=0; i<100; i++) 
    { 
     A(); 
     await Task.Delay(TimeSpan.FromSeconds(20)); 
     B(); 
     await Task.Delay(TimeSpan.FromSeconds(45)); 
     C(); 
     await Task.Delay(TimeSpan.FromMinutes(2)); 
    } 
} 

函数将立即返回,但该循环将继续在后台运行。

编辑:正如svick下面所述,这是一个新的.NET 4.5功能。

+0

很好的答案。这实际上就是我写的东西,但明确地使用了一个“Timer”,这实际上是一个令人讨厌的工作。 – 2013-02-24 19:27:34

+0

你可能想补充一点,你将需要VS2012。 – svick 2013-02-24 19:50:13