進度條與多線程


為什么需要進度條?

這里有200個左右的文件,每個文件里面有1000條數據庫插入語句,總共約200 000條記錄。

在單線程情況下,執行插入時界面會失去響應,完成插入需要長達8個小時。

不會有人認為界面沒有卡死,即使是我自己寫的程序,也不禁懷疑是否程序卡死。

進度條,以及各種執行情況顯示,在這種情況下真的很重要。

我還不明白為什么界面不能刷新,即使改變控件位置等等,因此目前只能采用多線程的方式實現進度條。

方式一

選擇控件

一個Button及ProgressBar

實現思路

由UI線程負責畫面刷新,另一個線程負責計算

窗體的字段this.count就是返回給進度條的數據

public partial class Form1 : Form
    {
        int count = 0;

這是模擬計算的函數,當然方法體也可以替換為實際的操作。

public void progress()//模擬執行一項耗時的操作
        {
            bool add = true;//進度條增長
            while (true)
            {
                while (add)
                {
                    if (this.count == 10000)//進度條上限
                    {
                        add = false;
                        break;
                    }
                    this.count += 100;//要操作的數據
                    Thread.Sleep(100);
                }
                while (!add)
                {
                    if (this.count == 0)//進度條下限
                    {
                        add = true;
                        break;
                    }
                    this.count -= 100;
                    Thread.Sleep(100);
                }
            }
        }

在Button的點擊事件中,將這個函數傳遞給線程

private void button1_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(new ThreadStart(progress));
            t.IsBackground = true;//在主界面關閉時此線程銷毀
            t.Start();
        }

此時點擊按鈕,this.count變化,但ProgressBar的Value沒變,要使Value變化,得不停的給Value賦值,因此增加一個Timer

private void timer1_Tick(object sender, EventArgs e)
        {
            this.progressBar1.Value = this.count;
        }

現在已經實現了進度條,並且由於是單獨的線程執行計算,界面並不會失去響應,任然是可互動的

結果

方式二

使用委托,將progress()傳遞給委托,另定義call方法,使用invoke方法解決調用線程無法訪問此對象的問題

public delegate void Draw();
Draw draw;

        private void button1_Click(object sender, EventArgs e)
        {
            draw = new Draw(progress);//在使用Invoke時才需要委托
            //Control.CheckForIllegalCrossThreadCalls = false;
            Thread t = new Thread(new ThreadStart(call));
            //Thread t = new Thread(new ThreadStart(progress));
            t.IsBackground = true;//在主界面關閉時此線程銷毀
            t.Start();
        }
         
        public void call()
        {
            while (true)
            {
                progressBar1.Invoke(draw);//似乎是提交給UI線程運行,UI線程在執行完自己代碼后才會執行Invoke提交的代碼
            }
        }

invoke方法雖然是在新創建的線程中調用的,但根據網上一些資料以及我自己的實驗,invoke應當還是由UI線程在執行,也就是說progress()由UI線程在執行,並沒有解決計算與更新分離的問題,雖然這是網上大部分資料的方式

而且這種方法界面雖然更新,但是沒有響應

改寫progress()

public void progress()//模擬執行一項耗時的操作
        {
            bool add = true;//進度條增長
            while (true)
            {
                while (add)
                {
                    if (this.count == 10000)
                    {
                        add = false;
                        break;
                    }
                    this.count += 100;
                    this.progressBar1.Value = this.count;//此處新增
                    Thread.Sleep(100);
                }
                while (!add)
                {
                    if (this.count == 0)
                    {
                        add = true;
                        break;
                    }
                    this.count -= 100;
                    this.progressBar1.Value = this.count;//此處新增
                    Thread.Sleep(100);
                }
            }
        }

停止使用Timer

結果

雖然實現了進度條,但是與界面不能互動,窗體處於無響應狀態

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM