【問題】

耗時ASP.NET Postback的傻瓜進度回報一文,雖然示範了跑迴圈定期呼叫ReportProgress委派傳回進度的貼心做法,實務上卻難以實現,例如: 某個Stored Procedure要跑一分鐘,在IDbCommand.ExecuteNonQuery()執行結束前,根本什麼都不能做,又如何能定期丟進度給前端?

    les.Execute( 
        sender as Button, 
        (rp) => 
            { 
                //模擬執行很耗時的作業 
                for (int i = 0; i < 5; i++) 
                { 
                    Thread.Sleep(1000); 
                    //處理到一個段落,呼叫Delegate傳回訊息 
                    rp("Process - " + i.ToString()); 
                } 
            }); 

【建議】

想在執行耗時動作時繼續擁有控制權,最簡單的做法是另開一條執行緒專門陪它玩,呼叫端可透過迴圈方式等待結果,在等待期間還能進行其他動作。不過除非有特殊設計,呼叫Stored Proceudre期間我們無從掌握進度也無法預測完成時間,能做的只有累計執行時間持續回報,讓使用者確認程式仍在執行中稍安勿躁。依據經驗,只要能提供線索(哪怕只是無意義的讀秒)讓使用者確認程式沒當掉,在使用體驗上便已遠勝"按鈕之後音訊全無"。

程式範例如下,原理是先宣告一個done旗標,然後開一條Thread專門執行耗時作業,結束時將done設為true;原來的Thread則跑while迴圈等待done變成true,等待期間重複Thread.Sleep 1秒後送一次執行秒數的動作,周而復始,如此就能達成呼叫Stored Procedure時持續傳回報的效果囉~

    protected void btnLongExec_Click(object sender, EventArgs e)
    {
        les.Execute(
            sender as Button,
            (rp) =>
                {
                    bool done = false;
                    int i = 0;
                    //另開一條Thread執行耗時作業,完成時將done設為true
                    Thread t = new Thread(() =>
                    {
                        //執行同步式耗時作業,例如執行Stored Procedure、大規模DB批次更新
                        //在此使用Thread.Sleep取代之
                            Thread.Sleep(20000);
                        done = true;
                    });
                    t.Start();
                    
                    //跑迴圈等候,每隔一秒傳回耗時統計(如有需要還可加入Timeout概念)
                        while (!done)
                    {
                        Thread.Sleep(1000);
                        i++;
                        rp("Process - " + i.ToString());
                    }
                });
    }

PS: 如果平台為.NET 4.5,非同步作業亦可用async/await實現,更為方便。


Comments

Be the first to post a comment

Post a comment