none
C# Timer被跳過不執行的原因 RRS feed

  • 問題

  • 各位好:

    以下是我的Code:

    private bool bool1;
    
    private void button2_Click(object sender, EventArgs e)
    {
        List<int> test = new List<int> { 1, 2, 3, 4 };
        foreach (int i in test)
        {
            Console.WriteLine("i = " + i);
            if (i < 3)
                bool1 = true;
            else
                bool1 = false;
             timer1.Start();
        }
    }
    
    private void timer1_Tick(object sender, EventArgs e)
    {
        if (bool1)
        {
            Console.WriteLine("Timer work for True");
            timer1.Stop();
        }
        else
        {
            Console.WriteLine("Timer work for False");
            timer1.Stop();
        }
    }


    輸出為:

    i = 1
    i = 2
    i = 3
    i = 4
    Timer work for False


    我想問為什麼輸出不是:

    i = 1
    Timer work for True
    i = 2
    Timer work for True
    i = 3
    Timer work for False
    i = 4
    Timer work for False

    什麼原因導致Timer不執行?



    • 已編輯 流宇星 2020年9月19日 上午 06:22
    2020年9月18日 下午 08:42

解答

  • (1) 即使你設定成 1,在一般的狀況下,timer 間閣依然會大於 15ms (假設你是 Windows 7 以上 ,預設時間解析度是 15.625 ms)

    (2) 就算可以設定成 1 好了,你的運算事實上也低於 1ms 甚多

    (3) 解釋一下你的程式運作 ,按下 button 後

    開始迭代,首先 i =1 ;
    Console.WriteLine("i = " + i); 印出 i = 1;

    因為 i < 3 所以 bool1 是 true

    啟動 timer,假設為 16ms

    因此在這 16ms 之間,就執行了 i=(bool1=true)2, i= 3(bool1=false), i =4  (bool1= false).... (但 timer 未曾啟動)

    直到 16ms 後, timer.tick 發生了,方法才進到佇列,才被執行一次


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。 https://skilltree.my/


    2020年9月19日 上午 04:58
    版主
  • 所以Timer是開啟另一個執行緒,造成foreach的執行不會因為Timer而暫停

    而因為Timer的啟動時間約須16ms,這段時間foreach已經跑完了,所以Timer啟動時 i = 4

    這樣理解對嗎?

    而程式之所以這樣寫,是為了將其他程式的狀況複製過來,所以Timer是必要的



    你用的應該是 Forms.Timer,它的事件委派函式會在 Form 所在的執行緒執行,並非多執行緒 (因為你是在 Form 裡宣告該 Timer)

    Forms.Timer 的 Tick 事件發生的時候,才會將其對應的委派函式放進訊息佇列,以你的程式來說 foreach 整個跑完的時候,那個方法根本還沒進到佇列中。更何況,button2_Click method 是先進到佇列的方法,這個方法沒結束,根本輪不到 Timer1_tick方法執行 (除非用上 Application.DoEvents 強迫佇列清空),我前面忘了提這一點,恐怕這個才是真正的重點。

    所以用 Forms.Timer 就是一個錯誤的開始,如果要以這個錯誤的開頭繼續解問題,我想大概是徒增困擾。


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。 https://skilltree.my/







    2020年9月19日 下午 01:35
    版主
  • 如果要硬搞的話,就是除了開始的動作之外,其他全都要移動到 Timer_Tick 方法去,像這樣 (但我是不贊成這樣搞啦)

    private int index = 0; List<int> test = new List<int> { 1, 2, 3, 4 }; private void button1_Click(object sender, EventArgs e) { Console.WriteLine("i =" + test[index]); timer1.Start(); } private void timer1_Tick(object sender, EventArgs e) { if (test[index] < 3) { Console.WriteLine("Timer work for True"); } else { Console.WriteLine("Timer work for False"); }

      timer1.Stop(); index++; if (index < test.Count) { Console.WriteLine("i =" + test[index]); timer1.Start(); } }



    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。 https://skilltree.my/


    2020年9月19日 下午 02:35
    版主

所有回覆

  • 有可能是您的Timer尚未啟動(例如Interval設定為1000為1秒後啟動), 但是您的迴圈在Timer尚未啟動前很快就跑完了
    • 已編輯 tihsMVP 2020年9月19日 上午 01:00
    2020年9月19日 上午 12:59
  • 我嘗試將Timer的Interval設置為1,但還是一樣的結果

    foreach不是應該等待Timer執行完才跑下一個嗎?

    那我應該怎麼做才能放慢迴圈速度或是讓程式等待Timer確實跑完?


    • 已編輯 流宇星 2020年9月19日 上午 02:54
    2020年9月19日 上午 02:48
  • (1) 即使你設定成 1,在一般的狀況下,timer 間閣依然會大於 15ms (假設你是 Windows 7 以上 ,預設時間解析度是 15.625 ms)

    (2) 就算可以設定成 1 好了,你的運算事實上也低於 1ms 甚多

    (3) 解釋一下你的程式運作 ,按下 button 後

    開始迭代,首先 i =1 ;
    Console.WriteLine("i = " + i); 印出 i = 1;

    因為 i < 3 所以 bool1 是 true

    啟動 timer,假設為 16ms

    因此在這 16ms 之間,就執行了 i=(bool1=true)2, i= 3(bool1=false), i =4  (bool1= false).... (但 timer 未曾啟動)

    直到 16ms 後, timer.tick 發生了,方法才進到佇列,才被執行一次


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。 https://skilltree.my/


    2020年9月19日 上午 04:58
    版主
  • 如果需求真的這麼簡單,那不如

     async private void button1_Click(object sender, EventArgs e)
     {
         List<int> test = new List<int> { 1, 2, 3, 4 };
         foreach (int i in test)
         {
             Console.WriteLine("i = " + i);
             if (i < 3)
                 Console.WriteLine("Timer work for True");
             else
                 Console.WriteLine("Timer work for false");
             await Task.Delay(100);
         }
    
     }


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。 https://skilltree.my/

    2020年9月19日 上午 05:01
    版主
  • 所以Timer是開啟另一個執行緒,造成foreach的執行不會因為Timer而暫停

    而因為Timer的啟動時間約須16ms,這段時間foreach已經跑完了,所以Timer啟動時 i = 4

    這樣理解對嗎?

    而程式之所以這樣寫,是為了將其他程式的狀況複製過來,所以Timer是必要的



    • 已編輯 流宇星 2020年9月19日 上午 06:19
    2020年9月19日 上午 06:18
  • 1) foreach不是應該等待Timer執行完才跑下一個嗎?

    沒有這種規定

    2) 您可以在Timer做完再使用ManualResetEvent 類別通知迴圈可以執行下一回合


    • 已編輯 tihsMVP 2020年9月19日 下午 12:50
    2020年9月19日 下午 12:50
  • 所以Timer是開啟另一個執行緒,造成foreach的執行不會因為Timer而暫停

    而因為Timer的啟動時間約須16ms,這段時間foreach已經跑完了,所以Timer啟動時 i = 4

    這樣理解對嗎?

    而程式之所以這樣寫,是為了將其他程式的狀況複製過來,所以Timer是必要的



    你用的應該是 Forms.Timer,它的事件委派函式會在 Form 所在的執行緒執行,並非多執行緒 (因為你是在 Form 裡宣告該 Timer)

    Forms.Timer 的 Tick 事件發生的時候,才會將其對應的委派函式放進訊息佇列,以你的程式來說 foreach 整個跑完的時候,那個方法根本還沒進到佇列中。更何況,button2_Click method 是先進到佇列的方法,這個方法沒結束,根本輪不到 Timer1_tick方法執行 (除非用上 Application.DoEvents 強迫佇列清空),我前面忘了提這一點,恐怕這個才是真正的重點。

    所以用 Forms.Timer 就是一個錯誤的開始,如果要以這個錯誤的開頭繼續解問題,我想大概是徒增困擾。


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。 https://skilltree.my/







    2020年9月19日 下午 01:35
    版主
  • 如果要硬搞的話,就是除了開始的動作之外,其他全都要移動到 Timer_Tick 方法去,像這樣 (但我是不贊成這樣搞啦)

    private int index = 0; List<int> test = new List<int> { 1, 2, 3, 4 }; private void button1_Click(object sender, EventArgs e) { Console.WriteLine("i =" + test[index]); timer1.Start(); } private void timer1_Tick(object sender, EventArgs e) { if (test[index] < 3) { Console.WriteLine("Timer work for True"); } else { Console.WriteLine("Timer work for False"); }

      timer1.Stop(); index++; if (index < test.Count) { Console.WriteLine("i =" + test[index]); timer1.Start(); } }



    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。 https://skilltree.my/


    2020年9月19日 下午 02:35
    版主
  • 哦~那這樣我大致上了解了,感謝Bill Chung和tihs兩位的說明!
    2020年9月20日 上午 06:58