2013-02-24 69 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