[C#][WinForm]UI別在無回應了
網友問題,這裡大概簡單實作並記錄。
一般初期大家都以單執行緒為主,但後期需求變多或要處理大量耗時作業
單一執行緒幾乎無法滿足我們的需求,而且還會被使用者抱怨程式用起來很卡或無回應(看個人造化!遇到好user算祖上積德XD)。
遇到這樣的狀況,大多使用者都會強制關閉程式(UI執行緒在沒把主控權交還給Windows前是凍結的)
使用單一執行緒幾乎都躲不了這樣的宿命,所以就得靠多執行緒來改善這樣的問題。
行為:執行大量新增作業(100000筆資料)
初始畫面
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。
使用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裡的物件也可即時重畫,在也不會發生無回應或很卡的狀況了。