none
請問多執行緒呼叫同一個function的問題。 RRS feed

  • 問題

  • 請問目前有個需求需在多執行緒下寫一個延時的功能。

    但假設我執行了三次,發現dtTmp會一直被更新成最後執行的時間,

                public Delay(int ms ,int incidate)
                {
                    DateTime dtTmp = DateTime.Now;
                    
                    double s;
                    try
                    {
                    
                       do
                       {
                        Application.DoEvents();
                       }while(DateTime.Now.Subtract(dtTmp).TotalMilliseconds<= ms);
    
                    }
                    catch (Exception)
                    {
    
                    }
                
                }
            }
    Cnt - 1000 Start Time:17:40:15:3524  <<第一次執行時間
    Cnt - 1000 Start Time:17:40:21:1479  <<第二次執行時間
    Cnt - 1000 Start Time:17:40:26:7969  <<第三次執行時間
    Cnt - 1000 End Time:17:40:36:7980    
    Cnt - 1000 End Time:17:40:36:7980    
    Cnt - 1000 End Time:17:40:36:7989

    假設以上執行三次都會延時10秒,為何我前面三次的執行開始時間不同,但結束的時間卻是一樣的,請問要如何解決,謝謝。

    2019年6月29日 上午 09:43

解答

  • 至於你原來寫法的問題,是因為你在 UI Thread 使用了 Thread.Sleep 導致 UI Thread 整個被封鎖了。

    也就是說,所有人都要等到最後一個封鎖解除,才有辦法執行到下一段,於是三個 end 時間就會非常接近。


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

    • 已標示為解答 jeffkuo8321 2019年6月30日 上午 09:43
    2019年6月30日 上午 09:11
    版主
  • 那一篇其實有下文我沒有寫 ,基本上如果要包裝的話,最推的方式就是將 MyDelay 包裝成 Async method 。

    因為該樓主問可以不要 async/await 嗎 ? 所以我才寫給他看, 但這樣的寫法後續有很多要處理,所以我沒寫下文,造成誤會了,抱歉。

     MyDelay  要類似這樣 (宣告為 async ,如果本來回傳是宣告 void 要改成回傳 Task ,如果回傳本來是 int 要改成 Task<int> )

        public class MyClass
        {
            async public static Task MyDelay(int i)
            {
                await Task.Delay(i);            
            }
        }
    呼叫端則是這樣

       async private void button1_Click(object sender, EventArgs e)
            {
                label1.Text = DateTime.Now.ToString("HH:mm:ss");
                await MyClass.MyDelay(1000);
                label2.Text = DateTime.Now.ToString("HH:mm:ss");
    
            }




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

    • 已標示為解答 jeffkuo8321 2019年7月8日 上午 10:09
    2019年7月1日 上午 10:00
    版主
  • 容我用多一點的文字描述整個流程,只用兩個Button,等待時間為 10 秒,略去 click 以外的事情和時間精準度,勞你補充有甚麼缺漏。


    0 秒 
    使用者按下 Button1
    執行 Button1_Click
    執行 Th1 Start 印出
    進入 DelayTime (就稱它叫 Delay1 吧)
    執行迴圈的內容 (sleep -> doevents 一直繞圈圈)
    跑跑跑 ..直到

    1 秒
    使用者按下 Button2
    等到 Dealy1 內迴圈執行 Application.DoEvents
    執行 Butto2_Click (Delay1 的迴圈會等待 Button2_Click 方法返回才會繼續往下走)
    執行 Th2 Start 印出
    進入 DelayTime (就稱它叫 Delay2 )
    執行迴圈內容 (sleep -> doevents 一直繞圈圈,我原來的意思是想說每個人都得等它結束,可能講的不周全)
    跑跑跑 ..直到

    11 秒
    Delay2 內迴圈結束
    Delay2 結束,返回 Button2_Click
    執行 Th2 End 印出
    Button2_Click 結束,返回 Delay1 的迴圈執行 Application.DoEvents 以後的程式碼
    因為 11 已經大於 10 (也就是 Delay1 應該結束的時間) 所以離開 Delay1 的迴圈
    Dealy1 結束,返回 Button1_Click
    印出 Th1 End


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





    2019年7月1日 下午 02:27
    版主
  • 容我用多一點的文字描述整個流程,只用兩個Button,等待時間為 10 秒,略去 click 以外的事情和時間精準度,勞你補充有甚麼缺漏。

    ......


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





    我从始至终不就是这个意思么?Application.DoEvents导致了多线程假象,实际上就一个线程。如果不使用多线程,仅在一个线程上延时,同时又不希望阻止UI,这是不可能的,完全不修改现有代码的话不可能实现。
    • 已標示為解答 jeffkuo8321 2019年7月1日 下午 11:33
    2019年7月1日 下午 02:40
  • 是的,可以裡面又需要有delay function,所以才會有之前的議題。

    请注意,你后面问的和前面的是不一样的,后面问的是在辅助线程上delay,前面问的是在UI线程上delay,这需要完全不同的处理方法。

    在UI线程上,为了不阻塞用户的操作,因此使用async/await。之所以使用这个,是为了方便,因为这是微软已经写好了的全自动线程池。不需要再用Thread,你找到的哪几篇文章所说的是完全正确的,使用了async/await后就不需要再new Thread了,TPL帮你做了这些事,TPL自己去new Thread。

    在辅助线程上,不存在阻塞用户操作问题,因此不需要async/await。此时如果需要delay,小于100ms时,用Sleep就可以了。

    • 已標示為解答 jeffkuo8321 2019年7月8日 下午 01:32
    2019年7月8日 下午 01:24
  • 1. 不需要DoEvent,只需要Sleep。

    2. SerialPort.ReadByte内部包含一个ManualResetEvent。只有当有数据时,ReadByte才会返回,没有数据时线程会被锁住。使用ReadByte读取数据时,不需要自行判断是否有数据需要接收,微软已经在SerialPort.ReadByte内部实现了这一判断,而且也实现了对线程的阻塞。

    • 已標示為解答 jeffkuo8321 2019年7月8日 下午 01:53
    2019年7月8日 下午 01:47

所有回覆

  • (1) 延時用 Task.Delay 比較好用。參考相關討論
    https://social.msdn.microsoft.com/Forums/zh-TW/8c7dcbe0-8f9d-4441-befc-97daec522d7c/await-taskdelay-?forum=233

    https://social.msdn.microsoft.com/Forums/zh-TW/51a2c1cd-4415-48c3-9e03-69e8a9566cd1/c-2431026178-3553125945?forum=233

    (2) 盡量不要使用 Applications.DoEvents,他會造成流程上的一些改變

    參考: https://dotblogs.com.tw/billchung/2013/01/19/87633


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

    • 已標示為解答 jeffkuo8321 2019年6月29日 下午 01:19
    • 已取消標示為解答 jeffkuo8321 2019年6月30日 上午 08:36
    2019年6月29日 上午 11:53
    版主
  • 謝謝大大回覆,暫時試了 await Task.Delay(i); 可以用,先這樣子用,另外請問就是我指的那個執行了三次,卻一起結束的問題,是因為DoEvents的關係嗎?
    2019年6月29日 下午 01:18
  • 謝謝大大回覆,暫時試了 await Task.Delay(i); 可以用,先這樣子用,另外請問就是我指的那個執行了三次,卻一起結束的問題,是因為DoEvents的關係嗎?

    需要有呼叫端的程式碼上下文才比較好判斷。


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


    2019年6月29日 下午 02:48
    版主
  • 以目前的 Framework 版本而言, 是鼓勵多使用  Task.Delay 取代其他的延時方式 (例如  Thread.Sleep)

    另外,同時也鼓勵使用 TPL 相關函式庫替代自行建立 Thread

    參考: 工作平行程式庫 (TPL)


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

    2019年6月29日 下午 02:51
    版主
  • 好的,我整理一下看怎麼發問再PO上來,感謝bill 大大的回覆. TPL來學習一下。
    2019年6月29日 下午 02:58
  • 以目前的 Framework 版本而言, 是鼓勵多使用  Task.Delay 取代其他的延時方式 (例如  Thread.Sleep)

    另外,同時也鼓勵使用 TPL 相關函式庫替代自行建立 Thread

    參考: 工作平行程式庫 (TPL)


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

    “Task.Delay取代其他方式”这种说法我认为不妥。Task.Delay是对System.Threading.Timer的一个封装,而Thread.Sleep是对Windows API Sleep的封装,用于阻塞当前线程。这二者是不同的功能,Thread.Sleep是效率很高的简单函数,但它不支持取消操作,Task.Delay内部创建Timer,成本高但支持取消操作。如果是在后台线程中使用,且不需要支持取消时应该优先考虑Thread.Sleep。如果是重复性的延时,则应直接创建一个Timer,然后反复使用这个Timer,而不是反复调用Task.Delay,Task.Delay只应该被用在debug和偶尔使用到的延迟中。
    2019年6月29日 下午 03:31
  • public void Delayms( Int32 ms)
            {
                try
                {
                    DateTime dtTmp=DateTime.Now;
    
                    do
                    {
    
                        Thread.Sleep(10);
                        Application.DoEvents();
                    
                    }while(DateTime.Now.Subtract(dtTmp).TotalMilliseconds<=ms);
    
    
    
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                 Debug.WriteLine("Th1 Start:" +DateTime.Now.ToString("hh:mm:ss"));
                    Delayms(10000);
                    Debug.WriteLine("Th1 End:" +DateTime.Now.ToString("hh:mm:ss"));
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
                 Debug.WriteLine("Th2 Start:" +DateTime.Now.ToString("hh:mm:ss"));
                    Delayms(10000);
                    Debug.WriteLine("Th2 End:" +DateTime.Now.ToString("hh:mm:ss"));
            }
    
            private void button3_Click(object sender, EventArgs e)
            {
                 Debug.WriteLine("Th3 Start:" +DateTime.Now.ToString("hh:mm:ss"));
                    Delayms(10000);
                    Debug.WriteLine("Th3 End:" +DateTime.Now.ToString("hh:mm:ss"));
            }

    以上是我寫的測試code,就是開一個form,然後三個按鈕,分別間隔個幾秒去按一次,然後停10秒,想請教為何我start時間是不一樣的(這沒錯),但end時間卻都會是一樣的。

    剛才把之前那個task.delay加在form 裡,發現會hand住,動不了,所以我在我寫的這個 delayms function裡加了

    application.DoEvents(),是不會hand住,可是結果就變成不是我要的了。如果我把DoEvents()從我寫的這個delayms

    function拿掉,只留下sleep(10),發現也會hand住,還請各位大師回覆,謝謝。

    Th1 Start:04:29:34
    Th2 Start:04:29:38
    Th3 Start:04:29:41

    Th3 End:04:29:54
    Th2 End:04:29:54
    Th1 End:04:29:54

    2019年6月30日 上午 08:37
  • 這麼簡單的需求我會這樣寫,這個寫法並不會封鎖住畫面。

        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            async private void button1_Click(object sender, EventArgs e)
            {
                Debug.WriteLine("Th1 Start:" + DateTime.Now.ToString("hh:mm:ss"));
                await Task.Delay(10000);
                Debug.WriteLine("Th1 End:" + DateTime.Now.ToString("hh:mm:ss"));
    
            }
    
            async private void button2_Click(object sender, EventArgs e)
            {
                Debug.WriteLine("Th2 Start:" + DateTime.Now.ToString("hh:mm:ss"));
                await Task.Delay(10000);
                Debug.WriteLine("Th2 End" + DateTime.Now.ToString("hh:mm:ss"));
            }
    
            async private void button3_Click(object sender, EventArgs e)
            {
    
                Debug.WriteLine("Th3 Start:" + DateTime.Now.ToString("hh:mm:ss"));
                await Task.Delay(10000);
                Debug.WriteLine("Th3 End" + DateTime.Now.ToString("hh:mm:ss"));
            }
        }


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

    2019年6月30日 上午 09:00
    版主
  • 至於你原來寫法的問題,是因為你在 UI Thread 使用了 Thread.Sleep 導致 UI Thread 整個被封鎖了。

    也就是說,所有人都要等到最後一個封鎖解除,才有辦法執行到下一段,於是三個 end 時間就會非常接近。


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

    • 已標示為解答 jeffkuo8321 2019年6月30日 上午 09:43
    2019年6月30日 上午 09:11
    版主
  • 看來不止 DoEvents要小心使用,連Sleep也要小心使用,感謝bill大大的回覆。
    2019年6月30日 上午 09:44
  • Thread.Sleep 會封鎖執行緒,所以在 UI Thread 不建議用 Thread.Sleep

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


    2019年6月30日 上午 09:48
    版主
  • 至於你原來寫法的問題,是因為你在 UI Thread 使用了 Thread.Sleep 導致 UI Thread 整個被封鎖了。

    也就是說,所有人都要等到最後一個封鎖解除,才有辦法執行到下一段,於是三個 end 時間就會非常接近。


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

    不是这个原因,这种解释是不正确的。Application.DoEvents是立即执行已经排队的消息,这其中就包括鼠标消息,同时这也会导致与之相关的事件立即执行。因此当你在点击第二个和第三个button时,其click事件会在Application.DoEvents处展开。因此经过展开之后原有的代码相当于:

    Debug.WriteLine("Th1 Start:" + DateTime.Now.ToString("hh:mm:ss"));
    Delayms(3000, "1");
    Debug.WriteLine("Th2 Start:" + DateTime.Now.ToString("hh:mm:ss"));
    Delayms(3000, "2");
    Debug.WriteLine("Th3 Start:" + DateTime.Now.ToString("hh:mm:ss"));
    Delayms(3000, "3");
    Debug.WriteLine("Th3 End:" + DateTime.Now.ToString("hh:mm:ss"));
    Debug.WriteLine("Th2 End:" + DateTime.Now.ToString("hh:mm:ss"));
    Debug.WriteLine("Th1 End:" + DateTime.Now.ToString("hh:mm:ss"));

    所以你最终看到的是Th3、Th2、Th1这样一个顺序的输出。

    2019年6月30日 上午 11:50
  • 我不覺得你的解釋和我有甚麼差異,你展開的程式碼還是要等待最後一個 Thread.Sleep 對 UI 執行緒的封鎖.

    而且樓主說點的不是順序問題 (即使這個問題的確發生,但我也沒有在解釋那個問題),而是在問為何最後的時間會如此相近 ? 而且我根本沒有聊到 Application.DoEvents 吧 ?


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




    2019年6月30日 下午 12:53
    版主
  • 謝謝bill大大的解釋,之前一直覺得可以用就好,一直沒時間回來看深入的問題,看來有些問題還是不能一直放著,哈哈。
    2019年6月30日 下午 01:17
  • 我不覺得你的解釋和我有甚麼差異,你展開的程式碼還是要等待最後一個 Thread.Sleep 對 UI 執行緒的封鎖.

    而且樓主說點的不是順序問題 (即使這個問題的確發生,但我也沒有在解釋那個問題),而是在問為何最後的時間會如此相近 ? 而且我根本沒有聊到 Application.DoEvents 吧 ?


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




    差异在于Sleep并没有导致时间问题,而且最后的时间不是接近,而是那三条语句是连续执行的。这里由于只有一个线程,所以无论是否有Sleep,对这个问题都没有影响。这不是线程同步导致的时间接近问题,而是语句的执行顺序问题,时间是一样的而不是接近。这个问题是由Application.DoEvents造成的,不是Sleep,不是Thread。实际上这个问题根本不涉及多个Thread,而正因为你的解释没有聊到Application.DoEvents,所以我说你的解释是不正确的,因为这根本就是Application.DoEvents问题,与Thread和Sleep没有任何关系。
    2019年6月30日 下午 01:53
  • 那三條語句連續執行, 難道印出來的時間不是叫接近,那不然要叫甚麼 ? 那三條之所以會連續執行在那個時間後,不就是因為 Thread Sleep ? 而且我關注的位置是三個 start 已經印出後的事情。

    樓主之所以會誤認的原因,就是因為他認為每一次所執行的 Sleep 迴圈結束後,就會馬上執行印出 "End" 的部分,所以他才會覺得為何最後三個時間是如此接近甚至相同。但事實上三個印出動作都是會在最後一個 Sleep 迴圈結束才陸續執行。

    by the way , 以下兩個圖來自於 Mastering in C# Concurrency 中一個 Web API 負載測試的結果
     上圖使用了 await Task.Delay , 下圖使用了 Thread.Sleep。可以看得出來 使用 Thread.Sleep 的 avg page response time 是是使用 await Task.Delay 的十倍。

     


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


    2019年6月30日 下午 02:07
    版主
  • 那三條語句連續執行, 難道印出來的時間不是叫接近,那不然要叫甚麼 ? 那三條之所以會連續執行在那個時間後,不就是因為 Thread Sleep ?

    樓主之所以會誤認的原因,就是因為他認為每一次所執行的 Sleep 迴圈結束後,就會馬上執行印出 "End" 的部分,所以他才會覺得為何最後三個時間是如此接近甚至相同。但事實上三個印出動作都是會在最後一個 Sleep 迴圈結束才陸續執行。

    by the way , 以下兩個圖來自於 Mastering in C# Concurrency 中一個 Web API 負載測試的結果
     上圖使用了 await Task.Delay , 下圖使用了 Thread.Sleep。可以看得出來 使用 Thread.Sleep 的 avg page response time 是是使用 await Task.Delay 的十倍。 


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

    你把Sleep那一句删掉,再运行就明白了。另外“接近”和“相等”不是同一个概念吧。
    2019年6月30日 下午 02:10
  • 负载测试想要说明什么问题?不明白?
    2019年6月30日 下午 02:13
  • 至於你原來寫法的問題,是因為你在 UI Thread 使用了 Thread.Sleep 導致 UI Thread 整個被封鎖了。

    也就是說,所有人都要等到最後一個封鎖解除,才有辦法執行到下一段,於是三個 end 時間就會非常接近。


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

    再請教一下如果我要包成function的話,有參考到您的另一個討論如下:
      public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
           private void button1_Click(object sender, EventArgs e)
            {
                label1.Text = DateTime.Now.ToString("HH:mm:ss");
                MyClass.MyDelay(1000);
                label2.Text = DateTime.Now.ToString("HH:mm:ss");
            }
    
            
        }
    
    
        public class MyClass
        {
            public static void MyDelay(int i)
            {
                var task = Task.Delay(i);
                task.Wait();
            }
        }
       但有發現如果在UI thread中呼叫,會造成UI hand住,但因為程式中會有多個class,或form如果寫成async的方式又怕移交程式的時候,可能要再去解釋這個方式為何,否有可以將async,await那種方式包成function,但又不造成UI hand住的方式,謝謝。

    2019年7月1日 上午 07:39
  • 那一篇其實有下文我沒有寫 ,基本上如果要包裝的話,最推的方式就是將 MyDelay 包裝成 Async method 。

    因為該樓主問可以不要 async/await 嗎 ? 所以我才寫給他看, 但這樣的寫法後續有很多要處理,所以我沒寫下文,造成誤會了,抱歉。

     MyDelay  要類似這樣 (宣告為 async ,如果本來回傳是宣告 void 要改成回傳 Task ,如果回傳本來是 int 要改成 Task<int> )

        public class MyClass
        {
            async public static Task MyDelay(int i)
            {
                await Task.Delay(i);            
            }
        }
    呼叫端則是這樣

       async private void button1_Click(object sender, EventArgs e)
            {
                label1.Text = DateTime.Now.ToString("HH:mm:ss");
                await MyClass.MyDelay(1000);
                label2.Text = DateTime.Now.ToString("HH:mm:ss");
    
            }




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

    • 已標示為解答 jeffkuo8321 2019年7月8日 上午 10:09
    2019年7月1日 上午 10:00
    版主
  • 怕程式碼混亂另外再貼一篇,如果硬是不要用 async/await 寫起來會多麻煩 (使用 async / await 的時候,C# 編譯器會幫你處理掉很多的麻煩)

      public class MyClass
        {
            public static void MyDelay(int i)
            {
                Task.Delay(i).Wait();
            }
        }

    呼叫端

            private void button1_Click(object sender, EventArgs e)
            {
                label1.Text = DateTime.Now.ToString("HH:mm:ss");
                var task1 = Task.Run(() => MyClass.MyDelay(1000));
                task1.ContinueWith((t) =>
                {
                    this.Invoke(new Action(() => label2.Text = DateTime.Now.ToString("HH:mm:ss")));
                });
    
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
                label3.Text = DateTime.Now.ToString("HH:mm:ss");
                var task1 = Task.Run(() => MyClass.MyDelay(1000));
                task1.ContinueWith((t) =>
                {
                    this.Invoke(new Action(() => label4.Text = DateTime.Now.ToString("HH:mm:ss")));
                });
    
            }



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


    2019年7月1日 上午 10:05
    版主
  • 怕程式碼混亂另外再貼一篇,如果硬是不要用 async/await 寫起來會多麻煩 (使用 async / await 的時候,C# 編譯器會幫你處理掉很多的麻煩)

      public class MyClass
        {
            public static void MyDelay(int i)
            {
                Task.Delay(i).Wait();
            }
        }

    呼叫端

            private void button1_Click(object sender, EventArgs e)
            {
                label1.Text = DateTime.Now.ToString("HH:mm:ss");
                var task1 = Task.Run(() => MyClass.MyDelay(1000));
                task1.ContinueWith((t) =>
                {
                    this.Invoke(new Action(() => label2.Text = DateTime.Now.ToString("HH:mm:ss")));
                });
    
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
                label3.Text = DateTime.Now.ToString("HH:mm:ss");
                var task1 = Task.Run(() => MyClass.MyDelay(1000));
                task1.ContinueWith((t) =>
                {
                    this.Invoke(new Action(() => label4.Text = DateTime.Now.ToString("HH:mm:ss")));
                });
    
            }



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


    ContinueWith的用法是传TaskScheduler,而不是调用Invoke。

    Task.Run(() =>
    {
        ......
    }).ContinueWith((task) =>
    {
        ......
    }, TaskScheduler.FromCurrentSynchronizationContext());


    • 已編輯 [-] 2019年7月1日 上午 11:19
    2019年7月1日 上午 11:18

  • ContinueWith的用法是传TaskScheduler,而不是调用Invoke。

    Task.Run(() =>
    {
        ......
    }).ContinueWith((task) =>
    {
        ......
    }, TaskScheduler.FromCurrentSynchronizationContext());


    thanks

    如果樓主對這些用法有興趣 ,有篇文章可以參考看看

    https://stackoverflow.com/questions/5818296/control-invoke-vs-tasks-with-a-taskscheduler


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



    2019年7月1日 上午 11:28
    版主
  • 如果樓主對這些用法有興趣 ,有篇文章可以參考看看

    https://stackoverflow.com/questions/5818296/control-invoke-vs-tasks-with-a-taskscheduler


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

    这篇文章讨论的内容是毫无意义的。因为这篇文章讨论的是在Task.Factory.StartNew中使用uiScheduler(例如来自TaskScheduler.FromCurrentSynchronizationContext())。StartNew直接调用UI线程,这等于什么意义都没有,微软已经发现此问题,因此提供了Task.Run来代替Task.Factory.StartNew,之所以提供Task.Run就是为了避免有人错误的在StartNew中使用uiScheduler。TaskScheduler只应该用在ContinueWith中,形成被微软称之为“任务链”的任务组。

    TaskScheduler.FromCurrentSynchronizationContext是对SynchronizationContext.Current的封装(https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/TaskScheduler.cs,a8790c1e6086ae5d)。

    补充:

    仔细看了一下那篇文章的时间,发现写于2011年,回复者指出“I think the best option, however, is to use a SynchronizationContext instance...”,这是因为当时.Net还没有开源,那位回复者不知道TaskScheduler其实就是SynchronizationContext,所以说这篇讨论在现在看来毫无意义。

    再补充一点:

    如果确定要使用ContinueWith,那上面的MyDelay就应该改写,如下。

    public static Task MyDelay(int i)
    {
        return Task.Delay(i);
    }
    
    MyDelay(100).ContinueWith((task) =>
    {
        ......
    }, TaskScheduler.FromCurrentSynchronizationContext());

    在Task.Run里面使用.Wait()等于多此一举,如果用Task.Run,那么时间小于100ms时应该首选Sleep,在后台线程中,短时间延迟不需要去创建并随后销毁Timer(也就是Task.Delay内部做的事)。

    • 已編輯 [-] 2019年7月1日 下午 01:01
    2019年7月1日 下午 12:32
  • 如果樓主對這些用法有興趣 ,有篇文章可以參考看看

    https://stackoverflow.com/questions/5818296/control-invoke-vs-tasks-with-a-taskscheduler


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

    这篇文章讨论的内容是毫无意义的。因为这篇文章讨论的是在Task.Factory.StartNew中使用uiScheduler(例如来自TaskScheduler.FromCurrentSynchronizationContext())。StartNew直接调用UI线程,这等于什么意义都没有,微软已经发现此问题,因此提供了Task.Run来代替Task.Factory.StartNew,之所以提供Task.Run就是为了避免有人错误的在StartNew中使用uiScheduler。TaskScheduler只应该用在ContinueWith中,形成被微软称之为“任务链”的任务组。

    TaskScheduler.FromCurrentSynchronizationContext是对SynchronizationContext.Current的封装(https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/TaskScheduler.cs,a8790c1e6086ae5d)。

    补充:

    仔细看了一下那篇文章的时间,发现写于2011年,回复者指出“I think the best option, however, is to use a SynchronizationContext instance...”,这是因为当时.Net还没有开源,那位回复者不知道TaskScheduler其实就是SynchronizationContext,所以说这篇讨论在现在看来毫无意义。

    再补充一点:

    如果确定要使用ContinueWith,那上面的MyDelay就应该改写,如下。

    public static Task MyDelay(int i)
    {
        return Task.Delay(i);
    }
    
    MyDelay(100).ContinueWith((task) =>
    {
        ......
    }, TaskScheduler.FromCurrentSynchronizationContext());

    在Task.Run里面使用.Wait()等于多此一举,如果用Task.Run,那么时间小于100ms时应该首选Sleep,在后台线程中,短时间延迟不需要去创建并随后销毁Timer(也就是Task.Delay内部做的事)。

    你搞錯對象,所以會用 Task.Delay().Wait 是為了說明如果硬是不要用 async / await 語句會搞成甚麼樣子。我前面的貼文也表明了我推薦的寫法是直接使用  async/await 語句。所以你這個寫法也是毫無意義。

    還有,我給樓主參考的意思是給發問者參考,對象不是你,如果你想戰那篇文有沒有意義, 你應該去 stackoverflow 上去戰。


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



    2019年7月1日 下午 01:24
    版主
  • 你搞錯對象,所以會用 Task.Delay().Wait 是為了說明如果硬是不要用 async / await 語句會搞成甚麼樣子。我前面的貼文也表明了我推薦的寫法是直接使用  async/await 語句。所以你這個寫法也是毫無意義。


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

    请问我哪里使用了async/await?

    OK, 這邊我看錯了,謝謝

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

    2019年7月1日 下午 01:34
    版主
  • 在Task.Run里面使用.Wait()等于多此一举,如果用Task.Run,那么时间小于100ms时应该首选Sleep,在后台线程中,短时间延迟不需要去创建并随后销毁Timer(也就是Task.Delay内部做的事)。

    你搞錯對象,所以會用 Task.Delay().Wait 是為了說明如果硬是不要用 async / await 語句會搞成甚麼樣子。我前面的貼文也表明了我推薦的寫法是直接使用  async/await 語句。所以你這個寫法也是毫無意義。

    還有,我給樓主參考的意思是給發問者參考,對象不是你,如果你想戰那篇文有沒有意義, 你應該去 stackoverflow 上去戰。


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



    请问我哪里使用了async/await?你是不是没有看清楚我写的内容?我说的是在Task.Run里面使用.Wait()等于多此一举,这个和async/await有什么关系呢?

    另外我什么时候反对过你给出链接了?我只说那篇文章已过时,内容在现在版本的.Net中已无任何意义。
    我只是在技术论坛上提出我的看法,什么叫“战”?这又不是政治论坛。难道我不应该指出问题来吗?不应该在这里讨论技术问题吗?

    2019年7月1日 下午 01:37
  • 至於你原來寫法的問題,是因為你在 UI Thread 使用了 Thread.Sleep 導致 UI Thread 整個被封鎖了。

    也就是說,所有人都要等到最後一個封鎖解除,才有辦法執行到下一段,於是三個 end 時間就會非常接近。


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

    不是这个原因,这种解释是不正确的。Application.DoEvents是立即执行已经排队的消息,这其中就包括鼠标消息,同时这也会导致与之相关的事件立即执行。因此当你在点击第二个和第三个button时,其click事件会在Application.DoEvents处展开。因此经过展开之后原有的代码相当于:

    Debug.WriteLine("Th1 Start:" + DateTime.Now.ToString("hh:mm:ss"));
    Delayms(3000, "1");
    Debug.WriteLine("Th2 Start:" + DateTime.Now.ToString("hh:mm:ss"));
    Delayms(3000, "2");
    Debug.WriteLine("Th3 Start:" + DateTime.Now.ToString("hh:mm:ss"));
    Delayms(3000, "3");
    Debug.WriteLine("Th3 End:" + DateTime.Now.ToString("hh:mm:ss"));
    Debug.WriteLine("Th2 End:" + DateTime.Now.ToString("hh:mm:ss"));
    Debug.WriteLine("Th1 End:" + DateTime.Now.ToString("hh:mm:ss"));

    所以你最终看到的是Th3、Th2、Th1这样一个顺序的输出。

    其實你這篇的展開也不盡然正確
    基本上在 Th3 End 印出結束,Th2 End 印出之前程序會回到 Dealy2 的內容再返回 ,接著印出 Th2 End;接著回到 Delay1 的內容再返回在印出 Th1 End。


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

    2019年7月1日 下午 01:40
    版主
  • 在Task.Run里面使用.Wait()等于多此一举,如果用Task.Run,那么时间小于100ms时应该首选Sleep,在后台线程中,短时间延迟不需要去创建并随后销毁Timer(也就是Task.Delay内部做的事)。

    你搞錯對象,所以會用 Task.Delay().Wait 是為了說明如果硬是不要用 async / await 語句會搞成甚麼樣子。我前面的貼文也表明了我推薦的寫法是直接使用  async/await 語句。所以你這個寫法也是毫無意義。

    還有,我給樓主參考的意思是給發問者參考,對象不是你,如果你想戰那篇文有沒有意義, 你應該去 stackoverflow 上去戰。


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



    请问我哪里使用了async/await?你是不是没有看清楚我写的内容?我说的是在Task.Run里面使用.Wait()等于多此一举,这个和async/await有什么关系呢?

    另外我什么时候反对过你给出链接了?我只说那篇文章已过时,内容在现在版本的.Net中已无任何意义。
    我只是在技术论坛上提出我的看法,什么叫“战”?这又不是政治论坛。难道我不应该指出问题来吗?不应该在这里讨论技术问题吗?

    我前面已經回文告訴你我看錯了,還謝謝你,你又把文刪除,這樣上下文變得很奇怪

    再次慎重地說: 謝謝你的指教,我會更謹慎。當下我就是很單純想要模擬出那個 method 是 void,或許這樣的思慮不夠周全。


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



    2019年7月1日 下午 01:41
    版主
  • 所以你最终看到的是Th3、Th2、Th1这样一个顺序的输出。

    其實你這篇的展開也不盡然正確
    基本上在 Th3 End 印出結束,Th2 End 印出之前程序會回到 Dealy2 的內容再返回 ,接著印出 Th2 End;接著回到 Delay1 的內容再返回在印出 Th1 End。


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

    你说的很对,因为全部展开要把所有do...while...全写一遍,所以我就示意性的展开了一下。在每一次延迟时间都相同,或后面的延迟时间比前面长时,在最后启动的do...while...中最后一次while为false时,外层的while必然也为false,因此最终三条End语句连在了一起。之前没有细说这点是因为,我想要说的是你用Sleep来解释是错误的,问题的关键点在于Application.DoEvents导致的展开,这和Sleep是没有关系的,想必你也试过删除Sleep了,很显然在单一线程中使用Sleep仅仅只会导致do...while...的次数减少,并不会改变逻辑顺序。至于是选择Sleep还是Task.Delay,还是Timer,应该根据实际情况决定,互相之间并非替代关系,这是我的看法。
    2019年7月1日 下午 01:54
  • 我前面已經回文告訴你我看錯了,還謝謝你,你又把文刪除,這樣上下文變得很奇怪

    再次慎重地說: 謝謝你的指教,我會更謹慎。當下我就是很單純想要模擬出那個 method 是 void,或許這樣的思慮不夠周全。


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



    删除了是因为和另一条回复合并在一起了,这点不是很重要吧,主要讨论技术就行了。

    我再解释一下,你前面用Sleep做解释很明显不正确,所以我说你的解释是错的(我总不能说这是正确的,对不对?),至于其它的只是我的看法和建议,如果你把这看作是针对你个人的批评,那肯定是误会了,绝无此意。

    • 已編輯 [-] 2019年7月1日 下午 02:15
    2019年7月1日 下午 01:55
  • 容我用多一點的文字描述整個流程,只用兩個Button,等待時間為 10 秒,略去 click 以外的事情和時間精準度,勞你補充有甚麼缺漏。


    0 秒 
    使用者按下 Button1
    執行 Button1_Click
    執行 Th1 Start 印出
    進入 DelayTime (就稱它叫 Delay1 吧)
    執行迴圈的內容 (sleep -> doevents 一直繞圈圈)
    跑跑跑 ..直到

    1 秒
    使用者按下 Button2
    等到 Dealy1 內迴圈執行 Application.DoEvents
    執行 Butto2_Click (Delay1 的迴圈會等待 Button2_Click 方法返回才會繼續往下走)
    執行 Th2 Start 印出
    進入 DelayTime (就稱它叫 Delay2 )
    執行迴圈內容 (sleep -> doevents 一直繞圈圈,我原來的意思是想說每個人都得等它結束,可能講的不周全)
    跑跑跑 ..直到

    11 秒
    Delay2 內迴圈結束
    Delay2 結束,返回 Button2_Click
    執行 Th2 End 印出
    Button2_Click 結束,返回 Delay1 的迴圈執行 Application.DoEvents 以後的程式碼
    因為 11 已經大於 10 (也就是 Delay1 應該結束的時間) 所以離開 Delay1 的迴圈
    Dealy1 結束,返回 Button1_Click
    印出 Th1 End


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





    2019年7月1日 下午 02:27
    版主
  • 容我用多一點的文字描述整個流程,只用兩個Button,等待時間為 10 秒,略去 click 以外的事情和時間精準度,勞你補充有甚麼缺漏。

    ......


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





    我从始至终不就是这个意思么?Application.DoEvents导致了多线程假象,实际上就一个线程。如果不使用多线程,仅在一个线程上延时,同时又不希望阻止UI,这是不可能的,完全不修改现有代码的话不可能实现。
    • 已標示為解答 jeffkuo8321 2019年7月1日 下午 11:33
    2019年7月1日 下午 02:40


  • 我从始至终不就是这个意思么?Application.DoEvents导致了多线程假象,实际上就一个线程。如果不使用多线程,仅在一个线程上延时,同时又不希望阻止UI,这是不可能的,完全不修改现有代码的话不可能实现。

    我是希望能夠透過文字的描述,讓發問者能更看清楚流程的走法而已。


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

    2019年7月1日 下午 02:48
    版主
  • 感謝二位大大的回覆,現在瞭解到當user按下按鈕時,在沒有特別利用多執行緒的方式狀況下,在UI 中的thread其實是單一thread,就像另一位大大講的,Application.DoEvents造成了多執行緒的假象(一直以來都認為UI event發生時,UI會在背景裡建立新的執行緒,因為在debug時,有打開平行堆疊去觀察,也的確有看到按了二次button時,會有看到二個method在跑,可是卻沒發現它是被包在同一個thread裡的),bill大大的文字流程可以瞭解到 button1的click會等待 button2的click method結束返回後才繼續執行,所以在最後印出end time時,會發現click 1的end time會相近於 click 2的end time,看來codeing的習慣還是要修正一下,不能被不正確的方法帶著走.
    2019年7月1日 下午 11:33
  • Bill 大大,再請問一下假設我已經把delay包成一個 async method,但如果在一個新建的thread 中要呼叫這個 function,

    在新建這個thread 時,就會出現 Task threadScan()的回傳類型錯誤的問題,是否這個新建的thread無法使用 async Task

    的方式去new 他,目前卡關中,再麻煩您協助了,感謝。

    async Task init() { thread th= await new Thread ( threadScan); // <----這邊會有錯誤. th.IsBackground=true; th.Start(); } async Task threadScan() { while(true) {

    await MyCalss.myDelay(1000); Debug.WriteLin("new thread scanning"); }; }




    2019年7月8日 上午 10:35
  • 你不需要 new Thread 啊

    直接 await threadScan() 應該就行了吧。

    但我看不出來為什麼你要這樣做就是了,而且你的 threadScan 方法是無限迴圈,這就會無限等待了。


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


    2019年7月8日 上午 10:52
    版主
  • 那個thread Scan只是個大概的function,因為現在寫的程式會有一個主流程,會另外起很多的執行緒去跑個別的東西(這些執行緒大都,會在程式結束時才跟著退出)但又想在這些別的執行緒中用到delay function。
    照您的方式會卡在這個loop中,但這不是我要的結果,比如我可能會在 Main()中起三個thread,然後在這其他去跑個別的東西,然後在這三個thread中都想用到之前包起來的delay method.謝謝.


    2019年7月8日 上午 11:04
  • 呃,其實我還是不太懂你的意思,假設你從 Main thread 另外生出三個 thread ,意思這三個 thread 內容都要跑無限迴圈,但設定為 IsBackground 為 true,讓這三個 Thread 是隨著主要起始的 thread 結束而結束,是這樣嗎?

    如果上面的猜想是對的,如果是該三個 Thread 裡要 Delay ,你應該是在那三個 Thread 的迴圈內容中 Delay 才對,而不是在主 thread Delay 啊。

    另外你的專案到底是 Console 還是 Windows Forms 還是 WPF ?


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

    2019年7月8日 上午 11:28
    版主
  • 呃,其實我還是不太懂你的意思,假設你從 Main thread 另外生出三個 thread ,意思這三個 thread 內容都要跑無限迴圈,但設定為 IsBackground 為 true,讓這三個 Thread 是隨著主要起始的 thread 結束而結束,是這樣嗎?

    如果上面的猜想是對的,如果是該三個 Thread 裡要 Delay ,你應該是在那三個 Thread 的迴圈內容中 Delay 才對,而不是在主 thread Delay 啊。

    另外你的專案到底是 Console 還是 Windows Forms 還是 WPF ?


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

    是的。且我的專案是win form,所以我可以假定初始form就是主流程,在form load 或new form 時去new 三個thread,然後我的delay是要在這三個thread中個別使用沒錯,只是我要在 thread中的 do while使用delay時,不是也要把這個method定義成async 底層照之前的方式寫都沒錯,但在最上層要new thread時,就會出錯

    Thread th= new Thread( await thread1);
    使用上面方式時會跟我講"無法等候方法羣組".

    2019年7月8日 上午 11:45
  • 去找了幾篇文章大概的意思都是說要使用 async/await 不需要new thread,但我的問題是要在多thread中去透過async/await實現delay function,可能要請您指教一下,或許我這種new thread的方式也是不正確的。

    我目前的功能是要寫一個RS-232DLL用來控制外部IO模組。所以在底層會有一個thread一直去掃是否有資料進來,但因為在沒資料進怕來時怕會卡住,所以又要加上一個delay function,大概delay 個20 ms,所以才會把之前你告知的async/await方式包成delay method,之前在沒有用這種方式時,就是一直用DoEvent和thread.sleep去做delay.



    2019年7月8日 上午 11:57
  • Bill 大大,再請問一下假設我已經把delay包成一個 async method,但如果在一個新建的thread 中要呼叫這個 function,

    在新建這個thread 時,就會出現 Task threadScan()的回傳類型錯誤的問題,是否這個新建的thread無法使用 async Task

    的方式去new 他,目前卡關中,再麻煩您協助了,感謝。

    async Task init() { thread th= await new Thread ( threadScan); // <----這邊會有錯誤. th.IsBackground=true; th.Start(); } async Task threadScan() { while(true) {

    await MyCalss.myDelay(1000); Debug.WriteLin("new thread scanning"); }; }

    这个async/await是个语法糖,编译器帮你把它转换成ContinueWith那样的任务链。因此要想await,必须返回值是Task或Task<T>,返回值如果是void或其它的,都是不能await的。new Thread是call构造函数,构造函数不能await(当然任何非Task的函数都不能await)。


    写代码之前需要先搞清概念。Task是TPL中的东西,TPL是微软写好的一个全自动线程池,TPL自动管理线程的创建和调度。换句话说就是,你需要往里面放的是任务,而不是线程(任务和线程不是同一个概念),任务是可以await的,线程不能await。


    针对你的代码就是这样:


    async void threadScan(){......}


    var th = new Thread(threadScan);

    2019年7月8日 下午 12:01
  • 去找了幾篇文章大概的意思都是說要使用 async/await 不需要new thread,但我的問題是要在多thread中去透過async/await實現delay function,可能要請您指教一下,或許我這種new thread的方式也是不正確的。

    我目前的功能是要寫一個RS-232DLL用來控制外部IO模組。所以在底層會有一個thread一直去掃是否有資料進來,但因為在沒資料進怕來時怕會卡住,所以又要加上一個delay function,大概delay 個20 ms,所以才會把之前你告知的async/await方式包成delay method,之前在沒有用這種方式時,就是一直用DoEvent和thread.sleep去做delay.

    你用new Thread去调用threadScan时,threadScan已经运行在后台线程(辅助线程)上了,因此根本不需要再去async/await。而且对于20ms的时间来说直接用Sleep就行了。


    使用async/await时TPL会在线程池中再开新的辅助线程,在一个辅助线程中再开启辅助线程并等待,这样的操作毫无意义。我认为你没有理解async/await的工作原理。
    2019年7月8日 下午 12:16
  • 你所说的RS-232是不是就是serial port?如果是serial port,应该用new Thread,不应该用async/await。
    2019年7月8日 下午 12:23
  • 是的,可以裡面又需要有delay function,所以才會有之前的議題。
    2019年7月8日 下午 01:04
  • 是的,可以裡面又需要有delay function,所以才會有之前的議題。

    请注意,你后面问的和前面的是不一样的,后面问的是在辅助线程上delay,前面问的是在UI线程上delay,这需要完全不同的处理方法。

    在UI线程上,为了不阻塞用户的操作,因此使用async/await。之所以使用这个,是为了方便,因为这是微软已经写好了的全自动线程池。不需要再用Thread,你找到的哪几篇文章所说的是完全正确的,使用了async/await后就不需要再new Thread了,TPL帮你做了这些事,TPL自己去new Thread。

    在辅助线程上,不存在阻塞用户操作问题,因此不需要async/await。此时如果需要delay,小于100ms时,用Sleep就可以了。

    • 已標示為解答 jeffkuo8321 2019年7月8日 下午 01:32
    2019年7月8日 下午 01:24
  • 请问为何你不直接使用.Net里面的SerialPort.ReadByte这类方法去读取数据呢?为什么要自己写一个loop?
    2019年7月8日 下午 01:27
  • 用這類方式也是要有一個loop判斷目前是否有資料要接收,在沒有資料的狀況下,還是要有一個delay function,不然cpu的load會升高。我知道也可以用event trigger的方式去寫,但因為之前己經有寫好部份程式,所以暫時不想再重寫。
    2019年7月8日 下午 01:32
  • 那請問是否只要用sleep就好,不要加入DoEvent,這樣就不會有流程被改變的狀況了?感謝bill大大的回覆,更瞭解UI和non UI中對 task,thread的用法,只是TPL這塊還要再看一下文章,有問題再請教,再次感謝。
    2019年7月8日 下午 01:36
  • 1. 不需要DoEvent,只需要Sleep。

    2. SerialPort.ReadByte内部包含一个ManualResetEvent。只有当有数据时,ReadByte才会返回,没有数据时线程会被锁住。使用ReadByte读取数据时,不需要自行判断是否有数据需要接收,微软已经在SerialPort.ReadByte内部实现了这一判断,而且也实现了对线程的阻塞。

    • 已標示為解答 jeffkuo8321 2019年7月8日 下午 01:53
    2019年7月8日 下午 01:47
  • 不過它一次只讀回一個byte,如果有連續資料傳輸時應該還是要有個loop一直跑來組資料,來試試看,感謝大大回覆.


    2019年7月8日 下午 01:53
  • 來試試看,再次感覺 bill 大大.

    是 [-] 兄回你的,你弄錯人了。

    真是峰迴路轉,原來你要問的是 Serial Port 

    一般的 serial port 入門你可以參考一下這個

    這只是一些入門的說明,如果你需要更高深的技巧,例如高頻率的發送讀取之類的,那就是另外一回事了。


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


    2019年7月8日 下午 02:01
    版主
  • 哈哈,不小心看錯了,好的,我來看一下,謝謝。
    2019年7月9日 上午 03:16
  • 一般的 serial port 入門你可以參考一下這個

    建议

    首先这一系列文章还是很全面很有价值的,这点毫无疑问。这里仅提一条建议。

    在此文(https://dotblogs.com.tw/billchung/2012/02/01/67312)中有如下代码。

    while (receiving)
    {
        if (comport.BytesToRead > 0)
        {
            ......
        }
        Thread.Sleep(16);
    }

    由于本文写于2012年,当时.Net还未开源,所以可能当时采用了这种方法。如今.Net已经开源,已知在.Net中所有使用到Windows API ReadFile的地方全部实现为I/O Completion Ports,所以建议将此部分代码改为如下。

    while (receiving)
    {
        ......
    }

    在一个后台工作线程中if和sleep已无用处,没有数据时此工作线程将被ManualResetEvent锁住,直到一个IoCompletionPort收到ReadFile发来的数据并写入.Net Serial Port内部缓冲区后才会放行。因此if和sleep的工作其实微软都已经做了,而且是用Event和Completion Port做的,由此建议对这篇老文略加修改。

    2019年7月9日 上午 07:21
  • 有空翻翻看我的那些 USB-RS232 還在不在。

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

    2019年7月9日 上午 07:36
    版主