C#線程學習筆記一:線程基礎

    本筆記摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/18/Thread.html,記錄一下學習過程以備後續查用。html

    1、線程的介紹安全

    進程(Process)是應用程序的實例要使用的資源的一個集合,每一個應用程序都在各自的進程中運行來確保應用程序不受其餘應用程序的影響。多線程

    線程是進程中基本執行單元, 一個進程中能夠包含多個線程。在進程入口執行的第一個線程是一個進程的主線程,在.NET應用程序中,都是以Main()方法app

做爲程序的入口(線程是進程的執行單元,進程是線程的一個容器)。異步

    2、線程調度和優先級  函數

    Windows之因此被稱爲搶佔式多線程操做系統,是由於線程能夠在任意時間被搶佔,並調度另外一個線程。學習

    每一個線程都分配了從0~31的一個優先級,系統首先把高優先級的線程分配給CPU執行。ui

    Windows 支持7個相對線程優先級:Idle、Lowest、Below Normal、Normal、Above Normal、Highest和Time-Critical。Normal是默認的線程優先級,lua

然而在程序中能夠經過設置Thread的Priority屬性來改變線程的優先級,它的類型爲ThreadPriority枚舉類型:Lowest、BelowNormal、Normal、AboveNormalspa

和Highest,CLR爲本身保留了 Idle和Time-Critical優先級。

    枚舉值列表以下:

成員名稱 說明
Lowest 能夠將Thread置於其餘優先級線程以後。
BelowNormal 能夠將Thread置於Normal優先級線程以後Lowest優先級線程以前。
Normal

能夠將Thread置於AboveNormal優先級線程以後BelowNormal優先級線程以前。

默認狀況下,線程置於Normal優先級。

AboveNormal 能夠將Thread置於Highest優先級線程以後Normal優先級線程以前。
Highest 能夠將Thread置於其餘優先級線程以前。

    3、前臺線程和後臺線程

    在.NET中線程分爲前臺線程和後臺線程:

    一、主線程是程序開始時就執行的,若是你須要再建立線程,那麼建立的線程就是這個主線程的子線程,它是前臺線程。 

    二、子線程能夠是前臺線程也能夠是後臺線程。

    三、前臺線程必須所有執行完,即便主線程關閉掉,這時進程仍然存活。

    四、當全部前臺線程中止運行時,CLR會強制結束仍在運行的任何後臺線程,這些後臺線程直接被終止,不會拋出異常。

    五、前臺線程與後臺線程惟一的區別是後臺線程不會阻止進程終止,能夠在任什麼時候候將前臺線程修改成後臺線程。

        static void Main(string[] args)
        {
            ThreadType();
        }

        /// <summary>
        /// 前臺線程與後臺線程
        /// </summary>
        private static void ThreadType()
        {
            //建立一個新線程(默認爲前臺線程)
            Thread backThread = new Thread(Worker)
            {
                //將線程更改成後臺線程
                IsBackground = true
            };

            //經過Start方法啓動線程
            backThread.Start();

            //若是backThread是前臺線程,則應用程序5秒後才終止。
            //若是backThread是後臺線程,則應用程序當即終止。
            Console.WriteLine("It's from main thread.");
            //Console.Read();
        }

        private static void Worker()
        {
            //休息5秒
            Thread.Sleep(5000);
            Console.WriteLine("It's from worker thread.");
        }

    假如保留IsBackground = true;但又想繼續執行Worker()方法的話,能夠調用Thread.Join()方法來實現。Join()方法能保證主線程(前臺線程)在異步線程

Thread(後臺線程)運行結束後纔會運行。

    注1:一個線程在執行的過程當中,可能調用另外一個線程,前者能夠稱爲調用線程,後者成爲被調用線程。

    注2:Thread.Join()方法的使用場景:調用線程掛起,等待被調用線程執行完畢後,繼續執行。

    注3:被調用線程執行Join方法,告訴調用線程,你先暫停,我執行完了,你再執行。從而保證了前後關係。

        static void Main(string[] args)
        {
            ThreadStatusChange();
        }

        /// <summary>
        /// 線程狀態之間的轉換
        /// </summary>
        private static void ThreadStatusChange()
        {
            //建立一個新線程(默認爲前臺線程)
            Thread backThread = new Thread(Worker)
            {
                //將線程更改成後臺線程
                IsBackground = true
            };

            //經過Start方法啓動線程
            backThread.Start();

            //Join()方法能保證主線程(前臺線程)在異步線程Thread(後臺線程)運行結束後纔會運行
            backThread.Join();

            Console.WriteLine("It's from main thread.");
            Console.Read();
        }

        private static void Worker()
        {
            //休息5秒
            Thread.Sleep(5000);
            Console.WriteLine("It's from worker thread.");
        }

    運行結果以下:

    4、 Suspend和Resume方法

    這兩個方法在.NET Framework 1.0的時候就支持的方法,他們分別能夠掛起線程及恢復掛起的線程,但在.NET Framework 2.0之後的版本中這兩個方法都過期了。

    MSDN的解釋是這樣:

    警告:

    不要使用Suspend和Resume方法來同步線程的活動。您沒法知道掛起線程時它正在執行什麼代碼。若是您在安全權限評估期間掛起持有鎖的線程,

則AppDomain中的其餘線程可能被阻止。若是您在線程正在執行類構造函數時掛起它,則 AppDomain中嘗試使用該類的其餘線程將被阻止。這樣很容易發生死鎖。

        static void Main(string[] args)
        {
            ThreadResume();
        }

        /// <summary>
        /// 線程恢復
        /// </summary>
        private static void ThreadResume()
        {
            Thread thread = new Thread(ThreadSuspend)
            {
                Name = "Thread1"
            };
            thread.Start();
            Thread.Sleep(2000);
            Console.WriteLine("Main Thread is running.");
            //線程恢復
            thread.Resume();
            Console.Read();
        }

        /// <summary>
        /// 線程掛起
        /// </summary>
        private static void ThreadSuspend()
        {
            Console.WriteLine("Thread: {0} has been suspended.", Thread.CurrentThread.Name);
            //將當前線程掛起
            Thread.CurrentThread.Suspend();
            Console.WriteLine("Thread: {0} has been resumed.", Thread.CurrentThread.Name);
        }

    在上面這段代碼中Thread1線程是在主線程中恢復的,但當主線程發生異常時,這時候Thread1就會一直處於掛起狀態,此時Thread1所使用的資源就不能釋放

(除非強制終止進程),當其它的線程須要使用這快資源的時候, 頗有可能就會發生死鎖現象。

    上面一段代碼還存在一個隱患,假如把Thread.Sleep(2000);這段代碼註釋一下:

        static void Main(string[] args)
        {
            ThreadResume();
        }

        /// <summary>
        /// 線程恢復
        /// </summary>
        private static void ThreadResume()
        {
            Thread thread = new Thread(ThreadSuspend)
            {
                Name = "Thread1"
            };
            thread.Start();
            //Thread.Sleep(2000);
            Console.WriteLine("Main Thread is running.");
            //線程恢復
            thread.Resume();
            Console.Read();
        }

        /// <summary>
        /// 線程掛起
        /// </summary>
        private static void ThreadSuspend()
        {
            Console.WriteLine("Thread: {0} has been suspended.", Thread.CurrentThread.Name);
            //將當前線程掛起
            Thread.CurrentThread.Suspend();
            Console.WriteLine("Thread: {0} has been resumed.", Thread.CurrentThread.Name);
        }

    這個時候,主線程由於跑(運行)得太快,作完本身的事情去喚醒Thread1時,此時Thread1尚未掛起,而此時喚醒Thread1就會出現異常了。

    5、Abort和Interrupt方法

    Abort方法和Interrupt都是用來終止線程的,可是二者仍是有區別的:

    一、它們拋出的異常不同:Abort 方法拋出的異常是ThreadAbortException,Interrupt拋出的異常爲ThreadInterruptedException。

    二、調用Interrupt方法的線程以後能夠被喚醒,然而調用Abort方法的線程就直接被終止不能被喚醒了。

    下面演示Abort方法的使用:

        static void Main(string[] args)
        {
            //ThreadType();
            //ThreadStatusChange();
            //ThreadResume();
            ThreadAbort();
        }        
        
        /// <summary>
        /// 線程中斷(不可再喚醒)
        /// </summary>
        private static void ThreadAbort()
        {
            Thread threadAbort = new Thread(AbortMethod)
            {
                Name = "ThreadAbort"
            };
            threadAbort.Start();
            Thread.Sleep(1000);
            try
            {
                threadAbort.Abort();
            }
            catch
            {
                Console.WriteLine("1-> {0} exception happen in main thread.", Thread.CurrentThread.Name);
                Console.WriteLine("2-> {0} status is:{1} in main thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
            finally
            {
                Console.WriteLine("3-> {0} status is:{1} in main thread.", threadAbort.Name, threadAbort.ThreadState);
            }
            threadAbort.Join();
            Console.WriteLine("4-> {0} status is:{1}", threadAbort.Name, threadAbort.ThreadState);
            Console.Read();
        }

        /// <summary>
        /// Abort方法
        /// </summary>
        private static void AbortMethod()
        {
            try
            {
                Thread.Sleep(5000);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.GetType().Name);
                Console.WriteLine("5-> {0} exception happen in abort thread.", Thread.CurrentThread.Name);
                Console.WriteLine("6-> {0} status is:{1} in abort thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
            finally
            {
                Console.WriteLine("7-> {0} status is:{1} in abort thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
        }

    運行結果以下:

    從運行結果能夠看出,調用Abort方法的線程引起的異常類型爲ThreadAbortException,另外異常只會在調用Abort方法的線程中發生,而不會在主線程中拋出,

其次調用Abort方法後線程的狀態不是當即改變爲Aborted狀態,而是從AbortRequested->Aborted。 

    下面演示Interrupt方法的使用:

        static void Main(string[] args)
        {
            ThreadInterrupt();
        }
        
        /// <summary>
        /// 線程中斷(可再喚醒)
        /// </summary>
        static void ThreadInterrupt()
        {
            Thread threadInterrupt = new Thread(InterruptMethod)
            {
                Name = "ThreadInterrupt"
            };
            threadInterrupt.Start();
            threadInterrupt.Interrupt();
            threadInterrupt.Join();
            Console.WriteLine("1-> {0} status is:{1} ", threadInterrupt.Name, threadInterrupt.ThreadState);
            Console.Read();
        }

     
        /// <summary>
        /// Interrupt方法
        /// </summary>
        private static void InterruptMethod()
        {
            try
            {
                Thread.Sleep(5000);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.GetType().Name);
                Console.WriteLine("2-> {0} exception happen in interrupt thread.", Thread.CurrentThread.Name);
                Console.WriteLine("3-> {0} status is:{1} in interrupt thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
            finally
            {
                Console.WriteLine("4-> {0} status is:{1} in interrupt thread.", Thread.CurrentThread.Name, Thread.CurrentThread.ThreadState);
            }
        }

    運行結果以下:

    從結果中能夠獲得,調用Interrupt方法拋出的異常爲:ThreadInterruptException, 另外當調用Interrupt方法後線程的狀態應該是中斷的,可是從運行結果看,

此時的線程由於Join、Sleep方法而喚醒了線程。

    爲了進一步解釋調用Interrupt方法的線程能夠被喚醒, 咱們能夠在線程執行的方法中運用循環,若是線程能夠喚醒,則輸出結果中就必定會有循環的部分,

而調用Abort方法的線程則不會有循環的部分。

    下面代碼相信你們看後確定會更加理解兩個方法的區別:

        static void Main(string[] args)
        {
            //ThreadType();
            //ThreadStatusChange();
            //ThreadResume();
            //ThreadAbort();
            //ThreadInterrupt();
            ThreadWake();
        }

        /// <summary>
        /// 線程喚醒
        /// </summary>
        static void ThreadWake()
        {
            Thread threadWake = new Thread(WakeMethod);
            threadWake.Start();
            Thread.Sleep(100);

            threadWake.Interrupt();
            Thread.Sleep(3000);
            Console.WriteLine("1-> After finally block,the threadWake status is:{0}", threadWake.ThreadState);
            Console.Read();
        }

        /// <summary>
        /// Wake方法
        /// </summary>
        private static void WakeMethod()
        {
            for (int i = 0; i < 4; i++)
            {
                try
                {
                    Thread.Sleep(2000);
                    Console.WriteLine("2-> Thread is Running.");
                }
                catch (Exception ex)
                {
                    if (ex != null)
                    {
                        Console.WriteLine("3-> Exception {0} throw.", ex.GetType().Name);
                    }
                }
                finally
                {
                    Console.WriteLine("4-> Current thread status is:{0}", Thread.CurrentThread.ThreadState);
                }
            }
        }

    運行結果以下:

    若是把上面的threadWake.Interrupt();改成threadWake.Abort(); 運行結果爲:

    6、簡單線程的使用

    其實在上面介紹前臺線程和後臺線程的時候已經經過ThreadStart委託建立一個線程了,此時已經實現了一個多線程的一個過程。

    下面經過ParameterizedThreadStart委託的方式來實現多線程:

        static void Main(string[] args)
        {
            ThreadTypeUseParameterized();
        }

        /// <summary>
        /// 前臺線程與後臺線程(使用ParameterizedThreadStart委託的方式來實現多線程)
        /// </summary>
        private static void ThreadTypeUseParameterized()
        {
            //建立一個新線程(默認爲前臺線程)
            Thread backThread = new Thread(new ParameterizedThreadStart(Worker1));

            //經過Start方法啓動線程
            backThread.Start(123);

            //若是backThread是前臺線程,則應用程序5秒後才終止。
            //若是backThread是後臺線程,則應用程序當即終止。
            Console.WriteLine("It's from main thread.");
        }

        private static void Worker1(object parameter)
        {
            //休息5秒
            Thread.Sleep(5000);
            Console.WriteLine(parameter+" is from worker1 thread.");
            Console.Read();
        }

    運行結果以下:

 

相關文章
相關標籤/搜索