[C#][WinForm]UI別在無回應了

  • 26690
  • 0
  • C#
  • 2009-12-12

[C#][WinForm]UI別在無回應了

 網友問題,這裡大概簡單實作並記錄。

一般初期大家都以單執行緒為主,但後期需求變多或要處理大量耗時作業

單一執行緒幾乎無法滿足我們的需求,而且還會被使用者抱怨程式用起來很卡或無回應(看個人造化!遇到好user算祖上積德XD)。

遇到這樣的狀況,大多使用者都會強制關閉程式(UI執行緒在沒把主控權交還給Windows前是凍結的)

使用單一執行緒幾乎都躲不了這樣的宿命,所以就得靠多執行緒來改善這樣的問題。

  

行為:執行大量新增作業(100000筆資料) 

初始畫面

image

 DataTable dt;
 bool flag;
private void Form1_Load(object sender, EventArgs e)
        {
            InitData();
            ThreadPool.SetMinThreads(2, 5);
            ThreadPool.SetMaxThreads(4, 10);
        }   


private void InitData()
        {
            dt = new DataTable("table1");
            dt.Columns.Add("c1");
            dt.Columns.Add("c2");
            Random rnd = new Random();
            for (Int32 i = 1; i <= 10; i++)
            {
                DataRow row = dt.NewRow();
                row["c1"] = rnd.Next(1, 100);
                row["c2"] = "i_am_c2";
                dt.Rows.Add(row);
            }
            dataGridView1.DataSource = dt;  
        }

單一執行緒(新增資料)

private void button1_Click(object sender, EventArgs e)
        {           
            flag = true;          
            //單一執行緒
            AddRow();
            //將job丟入ThreadPool Queue
            //主緒繼續處理畫面
            //ThreadPool.QueueUserWorkItem(new WaitCallback(AddRow)); 
        }  

 private void AddRow()
        {
            if (flag)
            {
                for (Int32 i = 1; i <= 100000; i++)
                {
                    DataRow dr = dt.NewRow();
                    dr["c1"] = i.ToString();
                    dr["c2"] = "hello";
                    dt.Rows.Add(dr);
                    label1.Text = "總共筆數:" + dt.Rows.Count.ToString();
                    if (i % 500 == 0)
                    {                       
                        dataGridView1.FirstDisplayedScrollingRowIndex = dt.Rows.Count;
                    }
                }
            }
            else                          
                return;                         
        }           
        

執行畫面果然完全凍結,而且畫面也無法重畫(一片白),這時任誰都會想按下ctrl+alt+del。

image 

使用ThreadPool來改善

private void button1_Click(object sender, EventArgs e)
        {           
            flag = true;          
            //單一執行緒
            //AddRow();
            //將job丟入ThreadPool Queue
            //主緒繼續處理畫面
            ThreadPool.QueueUserWorkItem(new WaitCallback(AddRow)); 
        }  

//宣告delegate
delegate void MyInvoke(String status);
        private void UpdateLab(String status)
        {
            label1.Text = status;
        }       

        delegate void MyInvoke2(Int32 index);
        private void DisplayDataGridView(Int32 index)
        {           
            dataGridView1.FirstDisplayedScrollingRowIndex = index;
        }

private void AddRow(object arg)
        {           
            if (flag)
            {
                for (Int32 i = 1; i <= 100000; i++)
                {                   
                    DataRow dr = dt.NewRow();
                    dr["c1"] = i.ToString();
                    dr["c2"] = "hello";
                    dt.Rows.Add(dr);
                    MyInvoke mi = new MyInvoke(UpdateLab);
                    //使用主緒更新Label
                    Invoke(mi, "總共筆數:" + dt.Rows.Count.ToString());                    
                    Thread.Sleep(1);  
                    if (i % 500 == 0)
                    {
                        MyInvoke2 mi2 = new MyInvoke2(DisplayDataGridView);
                        //使用主緒更新DataGridView
                        Invoke(mi2,i);
                    }                    
                }
            }
            else                          
                return;            
        }

使用ThreadPool後,UI終於可隨意拖拉,UI裡的物件也可即時重畫,在也不會發生無回應或很卡的狀況了。

image image