1. 程式人生 > >淺解多執行緒(一)

淺解多執行緒(一)

多執行緒的相關概念


1.程序:是作業系統結構的基礎;是一個正在執行的程式;計算機中正在執行的程式例項;可以分配給處理器並由處理器執行的一個實體;由單一順序的執行顯示,一個當前狀態和一組相關的系統資源所描述的活動單元。

2.執行緒:執行緒是程式中一個單一的順序控制流程。是程式執行流的最小單元。另外,執行緒是程序中的一個實體,是被系統獨立排程和分派的基本單位,執行緒自己不擁有系統資源,只擁有一點在執行中必不可少的資源,但它可與同屬一個程序的其它執行緒共享程序所擁有的全部資源。一個執行緒可以建立和撤消另一個執行緒,同一程序中的多個執行緒之間可以併發執行。由於執行緒之間的相互制約,致使執行緒在執行中呈現出間斷性。執行緒也有就緒、阻塞和執行三種基本狀態。每一個程式都至少有一個執行緒,若程式只有一個執行緒,那就是程式本身。

3.多執行緒:在單個程式中同時執行多個執行緒完成不同的工作,稱為多執行緒。


小結:其實更容易理解一點程序與執行緒的話,可以舉這樣一個例子:把程序理解成為一個運營著的公司,然而每一個公司員工就可以叫做一個執行緒。每個公司至少要有一個員工,員工越多,如果你的管理合理的話,公司的運營速度就會越好。這裡官味一點話就是說。cpu大部分時間處於空閒時間,浪費了cpu資源,多執行緒可以讓一個程式“同時”處理多個事情,提高效率。


單執行緒問題演示


建立一個WinForm應用程式,這裡出現的問題是,點選按鈕後如果在彈出提示框之前,窗體是不能被拖動的。

複製程式碼
 private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 10000000000; i++)  
            {
                i += 1;
            }
            MessageBox.Show("出現後能拖動,提示沒出現之前窗體不能被拖動");
        }
複製程式碼

原因:執行這個應用程式的時候,窗體應用程式自帶一個叫做UI的執行緒,這個執行緒負責窗體介面的移動大小等。如果點選按鈕則這個執行緒就去處理這個迴圈計算,而放棄了其它操作,故而窗體拖動無響應。這就是單執行緒帶來的問題。

解決辦法:使用多執行緒,我們自己建立執行緒。把計算程式碼放入我們自己寫的執行緒中,UI執行緒就能繼續做他的介面響應了。


執行緒的建立


 執行緒的實現:執行緒一定是要執行一段程式碼的,所以要產生一個執行緒,必須先為該執行緒寫一個方法,這個方法中的程式碼,就是該執行緒中要執行的程式碼,然而啟動執行緒時,是通過委託呼叫該方法的。執行緒啟動是,呼叫傳過來的委託,委託就會執行相應的方法,從而實現執行緒執行方法。

複製程式碼
 //建立執行緒  
        private void button1_Click(object sender, EventArgs e)
        {
            //ThreadStart是一個無參無返回值的委託。
            ThreadStart ts = new ThreadStart(js);
            //初始化Thread的新例項,並通過構造方法將委託ts做為引數賦初始值。
            Thread td = new Thread(ts);   //需要引入System.Threading名稱空間
            //執行委託
            td.Start();
        }
        //建立的執行緒要執行的函式。
        void js()
        {
            for (int i = 0; i < 1000000000; i++)
            {
                i += 1;
            }
            MessageBox.Show("提示出現前後窗體都能被拖動");
        }
複製程式碼

把這個計算寫入自己寫的執行緒中,就解決了單執行緒中的介面無反應缺陷。


小結:建立執行緒的4個步驟:1.編寫執行緒索要執行的方法。2.引用System.Threading命名空。3.例項化Thread類,並傳入一個指向執行緒所要執行方法的委託。4.呼叫Start()方法,將該執行緒標記為可以執行的狀態,但具體執行時間由cpu決定。


 方法重入(多個執行緒執行一個方法)


 由於執行緒可與同屬一個程序的其它執行緒共享程序所擁有的全部資源。

所以多個執行緒同時執行一個方法的情況是存在的,然而這裡不經過處理的話會出現一點問題,執行緒之間先後爭搶資源,致使資料計算結果錯亂。 

複製程式碼
 public partial class 方法重入 : Form
    {
        public 方法重入()
        {
            InitializeComponent();

            //設定TextBox類的這個屬性是因為,開啟ui執行緒,
            //微軟設定檢測不允許其它執行緒對ui執行緒的資料進行訪問,這裡我們把檢測關閉,也就允許了其它執行緒對ui執行緒資料的訪問。
            //如果檢測不設定為False,則報錯。
            TextBox.CheckForIllegalCrossThreadCalls = false;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = "0";
            //開啟第一個執行緒,對js方法進行計算
            ThreadStart ts = new ThreadStart(js);
            Thread td = new Thread(ts);
            td.Start();

            //開啟第二個執行緒,對js方法進行計算
            ThreadStart ts1 = new ThreadStart(js);
            Thread td1 = new Thread(ts1);
            td1.Start();
        }
        //多執行緒要重入的方法。
        void js()
        {
            int a = Convert.ToInt32(textBox1.Text);
            for (int i = 0; i < 2000; i++)
            {
                a++;
                textBox1.Text = a.ToString();
            }
        }
    } 
複製程式碼

出錯現象:點選按鈕後TextBox1中資料為2000+或2000,如果你看到的資料一直是2000說明你的計算機cpu比較牛X,這樣的話你想看到不是2000的話,你可以多點選幾次試試,真不行的話,程式碼中給TextBox1賦值為0,換做在介面中給textBox1數值預設值為0試試看。

出錯原因:兩個程序同時計算這個方法,不相干擾應該每個執行緒計算的結果都是2000的,但是這裡的結果輸出卻讓人以外,原因是第一個兩個執行緒同時計算,並不是同時開始計算,而是根據cpu決定的哪個先開始,哪個後開始,雖然相差時間不多,但後開始的就會取用先開始計算過的資料計算,這樣就會導致計算錯亂。

解決辦法:解決這個的一個簡單辦法解釋給方法加鎖,加鎖的意思就是第一個執行緒取用過這個資源完畢後,第二個執行緒再來取用此資源。形成排隊效果。

下面給方法加鎖。

複製程式碼
//多執行緒要重入的方法,這裡加鎖。
        void js()
        {
            lock (this)
            {
                int a = Convert.ToInt32(textBox1.Text);
                for (int i = 0; i < 2000; i++)
                {
                    a++;
                    textBox1.Text = a.ToString();
                }
            }
        }
複製程式碼

給方法加過鎖後,執行緒一前一後取用資源,就能避免不可預計的錯亂結果,第一個執行緒計算為2000,第二個執行緒計算就是從2000開始,這裡的結果就為4000。


小結:多執行緒可以同時執行,提高了cpu的效率,這裡的同時並不是同時開始,同時結束,他們的開始是由cpu決定的,時間相差不大,但會有不可預計的計算錯亂,這裡要注意類似上面例子導致的方法重入問題。


 前臺執行緒後臺執行緒


 .Net的公用語言執行時能區分兩種不同型別的執行緒:前臺執行緒和後臺執行緒。這兩者的區別就是:應用程式必須執行完所有的前臺執行緒才可以退出;而對於後臺執行緒,應用程式則可以不考慮其是否已經執行完畢而直接退出,所有的後臺執行緒在應用程式退出時都會自動結束。

問題:關閉了視窗,訊息框還能彈出。

複製程式碼
  private void button1_Click(object sender, EventArgs e)
        { 
            //開啟一個執行緒,對js方法進行計算
            ThreadStart ts2 = new ThreadStart(js);
            Thread td2 = new Thread(ts2);             
            td2.Start();

        }        
        void js()
        {
            for (int i = 0; i < 2000000000; i++)  //如果看不出效果這裡的2後面多加0
            {
                i++;
            }
            MessageBox.Show("關閉了視窗我還是要出來的!");
        }
複製程式碼

原因:.Net環境使用Thread建立執行緒,執行緒預設為前臺執行緒。即執行緒屬性IsBackground=false,而前臺執行緒只要有一個在執行則應用程式不關閉,所以知道彈出訊息框後應用程式才算關閉。

解決辦法:在程式碼中設定td2.IsBackground=true;


 執行緒執行帶引數的方法


 

複製程式碼
 //建立一個執行帶引數方法的執行緒
        private void button1_Click(object sender, EventArgs e)
        {
            //ParameterizedThreadStart這是一個引數型別為object的委託
            ParameterizedThreadStart pts=new ParameterizedThreadStart(SayHello);
            Thread td2 = new Thread(pts);
            td2.Start("張三");  //引數值先入這裡
        }
        void SayHello(object name)
        {
            MessageBox.Show("你好,"+name.ToString()+"!");
        } 
複製程式碼

執行緒執行帶多引數的方法


 

 其實還是帶一引數的方法,只不過是利用引數型別為object的好處,這裡將型別傳為list型別,貌似多參。

複製程式碼
        //建立一個執行帶多個引數的方法執行緒
        private void button1_Click(object sender, EventArgs e)
        {
            List<string> list = new List<string> { "張三", "李四", "王五" };
            //ParameterizedThreadStart這是一個引數型別為object的委託
            ParameterizedThreadStart pts=new ParameterizedThreadStart(SayHello);
            Thread td2 = new Thread(pts);
            td2.Start(list);  //引數值先入這裡
        }
        void SayHello(object list)
        {
            List<string> lt = list as List<string>;
            for (int i = 0; i < lt.Count; i++)
            {
                MessageBox.Show("你好," + lt[i].ToString() + "!");
            }
        } 
複製程式碼

總結:看到這裡相信對多執行緒應該有一個初步的瞭解了,我就不說了。

 

 ************轉載:http://www.cnblogs.com/knowledgesea/archive/2012/11/22/2780651.html

多執行緒的相關概念


1.程序:是作業系統結構的基礎;是一個正在執行的程式;計算機中正在執行的程式例項;可以分配給處理器並由處理器執行的一個實體;由單一順序的執行顯示,一個當前狀態和一組相關的系統資源所描述的活動單元。

2.執行緒:執行緒是程式中一個單一的順序控制流程。是程式執行流的最小單元。另外,執行緒是程序中的一個實體,是被系統獨立排程和分派的基本單位,執行緒自己不擁有系統資源,只擁有一點在執行中必不可少的資源,但它可與同屬一個程序的其它執行緒共享程序所擁有的全部資源。一個執行緒可以建立和撤消另一個執行緒,同一程序中的多個執行緒之間可以併發執行。由於執行緒之間的相互制約,致使執行緒在執行中呈現出間斷性。執行緒也有就緒、阻塞和執行三種基本狀態。每一個程式都至少有一個執行緒,若程式只有一個執行緒,那就是程式本身。

3.多執行緒:在單個程式中同時執行多個執行緒完成不同的工作,稱為多執行緒。


小結:其實更容易理解一點程序與執行緒的話,可以舉這樣一個例子:把程序理解成為一個運營著的公司,然而每一個公司員工就可以叫做一個執行緒。每個公司至少要有一個員工,員工越多,如果你的管理合理的話,公司的運營速度就會越好。這裡官味一點話就是說。cpu大部分時間處於空閒時間,浪費了cpu資源,多執行緒可以讓一個程式“同時”處理多個事情,提高效率。


單執行緒問題演示


建立一個WinForm應用程式,這裡出現的問題是,點選按鈕後如果在彈出提示框之前,窗體是不能被拖動的。

複製程式碼
 private void button1_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 10000000000; i++)  
            {
                i += 1;
            }
            MessageBox.Show("出現後能拖動,提示沒出現之前窗體不能被拖動");
        }
複製程式碼

原因:執行這個應用程式的時候,窗體應用程式自帶一個叫做UI的執行緒,這個執行緒負責窗體介面的移動大小等。如果點選按鈕則這個執行緒就去處理這個迴圈計算,而放棄了其它操作,故而窗體拖動無響應。這就是單執行緒帶來的問題。

解決辦法:使用多執行緒,我們自己建立執行緒。把計算程式碼放入我們自己寫的執行緒中,UI執行緒就能繼續做他的介面響應了。


執行緒的建立


 執行緒的實現:執行緒一定是要執行一段程式碼的,所以要產生一個執行緒,必須先為該執行緒寫一個方法,這個方法中的程式碼,就是該執行緒中要執行的程式碼,然而啟動執行緒時,是通過委託呼叫該方法的。執行緒啟動是,呼叫傳過來的委託,委託就會執行相應的方法,從而實現執行緒執行方法。

複製程式碼
 //建立執行緒  
        private void button1_Click(object sender, EventArgs e)
        {
            //ThreadStart是一個無參無返回值的委託。
            ThreadStart ts = new ThreadStart(js);
            //初始化Thread的新例項,並通過構造方法將委託ts做為引數賦初始值。
            Thread td = new Thread(ts);   //需要引入System.Threading名稱空間
            //執行委託
            td.Start();
        }
        //建立的執行緒要執行的函式。
        void js()
        {
            for (int i = 0; i < 1000000000; i++)
            {
                i += 1;
            }
            MessageBox.Show("提示出現前後窗體都能被拖動");
        }
複製程式碼

把這個計算寫入自己寫的執行緒中,就解決了單執行緒中的介面無反應缺陷。


小結:建立執行緒的4個步驟:1.編寫執行緒索要執行的方法。2.引用System.Threading命名空。3.例項化Thread類,並傳入一個指向執行緒所要執行方法的委託。4.呼叫Start()方法,將該執行緒標記為可以執行的狀態,但具體執行時間由cpu決定。


 方法重入(多個執行緒執行一個方法)


 由於執行緒可與同屬一個程序的其它執行緒共享程序所擁有的全部資源。

所以多個執行緒同時執行一個方法的情況是存在的,然而這裡不經過處理的話會出現一點問題,執行緒之間先後爭搶資源,致使資料計算結果錯亂。 

複製程式碼
 public partial class 方法重入 : Form
    {
        public 方法重入()
        {
            InitializeComponent();

            //設定TextBox類的這個屬性是因為,開啟ui執行緒,
            //微軟設定檢測不允許其它執行緒對ui執行緒的資料進行訪問,這裡我們把檢測關閉,也就允許了其它執行緒對ui執行緒資料的訪問。
            //如果檢測不設定為False,則報錯。
            TextBox.CheckForIllegalCrossThreadCalls = false;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = "0";
            //開啟第一個執行緒,對js方法進行計算
            ThreadStart ts = new ThreadStart(js);
            Thread td = new Thread(ts);
            td.Start();

            //開啟第二個執行緒,對js方法進行計算
            ThreadStart ts1 = new ThreadStart(js);
            Thread td1 = new Thread(ts1);
            td1.Start();
        }
        //多執行緒要重入的方法。
        void js()
        {
            int a = Convert.ToInt32(textBox1.Text);
            for (int i = 0; i < 2000; i++)
            {
                a++;
                textBox1.Text = a.ToString();
            }
        }
    } 
複製程式碼

出錯現象:點選按鈕後TextBox1中資料為2000+或2000,如果你看到的資料一直是2000說明你的計算機cpu比較牛X,這樣的話你想看到不是2000的話,你可以多點選幾次試試,真不行的話,程式碼中給TextBox1賦值為0,換做在介面中給textBox1數值預設值為0試試看。

出錯原因:兩個程序同時計算這個方法,不相干擾應該每個執行緒計算的結果都是2000的,但是這裡的結果輸出卻讓人以外,原因是第一個兩個執行緒同時計算,並不是同時開始計算,而是根據cpu決定的哪個先開始,哪個後開始,雖然相差時間不多,但後開始的就會取用先開始計算過的資料計算,這樣就會導致計算錯亂。

解決辦法:解決這個的一個簡單辦法解釋給方法加鎖,加鎖的意思就是第一個執行緒取用過這個資源完畢後,第二個執行緒再來取用此資源。形成排隊效果。

下面給方法加鎖。

複製程式碼
//多執行緒要重入的方法,這裡加鎖。
        void js()
        {
            lock (this)
            {
                int a = Convert.ToInt32(textBox1.Text);
                for (int i = 0; i < 2000; i++)
                {
                    a++;
                    textBox1.Text = a.ToString();
                }
            }
        }
複製程式碼

給方法加過鎖後,執行緒一前一後取用資源,就能避免不可預計的錯亂結果,第一個執行緒計算為2000,第二個執行緒計算就是從2000開始,這裡的結果就為4000。


小結:多執行緒可以同時執行,提高了cpu的效率,這裡的同時並不是同時開始,同時結束,他們的開始是由cpu決定的,時間相差不大,但會有不可預計的計算錯亂,這裡要注意類似上面例子導致的方法重入問題。


 前臺執行緒後臺執行緒


 .Net的公用語言執行時能區分兩種不同型別的執行緒:前臺執行緒和後臺執行緒。這兩者的區別就是:應用程式必須執行完所有的前臺執行緒才可以退出;而對於後臺執行緒,應用程式則可以不考慮其是否已經執行完畢而直接退出,所有的後臺執行緒在應用程式退出時都會自動結束。

問題:關閉了視窗,訊息框還能彈出。

複製程式碼
  private void button1_Click(object sender, EventArgs e)
        { 
            //開啟一個執行緒,對js方法進行計算
            ThreadStart ts2 = new ThreadStart(js);
            Thread td2 = new Thread(ts2);             
            td2.Start();

        }        
        void js()
        {
            for (int i = 0; i < 2000000000; i++)  //如果看不出效果這裡的2後面多加0
            {
                i++;
            }
            MessageBox.Show("關閉了視窗我還是要出來的!");
        }
複製程式碼

原因:.Net環境使用Thread建立執行緒,執行緒預設為前臺執行緒。即執行緒屬性IsBackground=false,而前臺執行緒只要有一個在執行則應用程式不關閉,所以知道彈出訊息框後應用程式才算關閉。

解決辦法:在程式碼中設定td2.IsBackground=true;


 執行緒執行帶引數的方法


 

複製程式碼
 //建立一個執行帶引數方法的執行緒
        private void button1_Click(object sender, EventArgs e)
        {
            //ParameterizedThreadStart這是一個引數型別為object的委託
            ParameterizedThreadStart pts=new ParameterizedThreadStart(SayHello);
            Thread td2 = new Thread(pts);
            td2.Start("張三");  //引數值先入這裡
        }
        void SayHello(object name)
        {
            MessageBox.Show("你好,"+name.ToString()+"!");
        } 
複製程式碼

執行緒執行帶多引數的方法


 

 其實還是帶一引數的方法,只不過是利用引數型別為object的好處,這裡將型別傳為list型別,貌似多參。

複製程式碼
        //建立一個執行帶多個引數的方法執行緒
        private void button1_Click(object sender, EventArgs e)
        {
            List<string> list = new List<string> { "張三", "李四", "王五" };
            //ParameterizedThreadStart這是一個引數型別為object的委託
            ParameterizedThreadStart pts=new ParameterizedThreadStart(SayHello);
            Thread td2 = new Thread(pts);
            td2.Start(list);  //引數值先入這裡
        }
        void SayHello(object list)
        {
            List<string> lt = list as List<string>;
            for (int i = 0; i < lt.Count; i++)
            {
                MessageBox.Show("你好," + lt[i].ToString() + "!");
            }
        } 
複製程式碼

總結:看到這裡相信對多執行緒應該有一個初步的瞭解了,我就不說了。

 

 ************轉載:http://www.cnblogs.com/knowledgesea/archive/2012/11/22/2780651.html