Thread類(線程)


操作系統通過線程對程序的執行進行管理,當操作系統運行一個程序的時候,首先,操作系統將為這個准備運行的程序分配一個進程,以管理這個程序所需要的各種資源。在這些資源之中,會包含一個稱為主線程的線程數據結構,用來管理這個程序的執行狀態。

  在Windows操作系統下,線程的的數據結構包含以下內容:

  1、線程的核心對象:主要包含線程當前的寄存器狀態,當操作系統調度這個線程開始運行的時候,寄存器的狀態將被加載到CPU中,重新構建線程的執行環境,當線程被調度出來的時候,最后的寄存器狀態被重新保存到這里,已備下一次執行的時候使用。
  2、線程環境塊(Thread Environment Block,TED):是一塊用戶模式下的內存,包含線程的異常處理鏈的頭部。另外,線程的局部存儲數據(Thread Local Storage Data)也存在這里。
  3、用戶模式的堆棧:用戶程序的局部變量和參數傳遞所使用的堆棧,默認情況下,Windows將會被分配1M的空間用於用戶模式堆棧。
  4、內核模式堆棧:用於訪問操作系統時使用的堆棧。

在搶先式多任務的環境下,在一個特定的時間,CPU將一個線程調度進CPU中執行,這個線程最多將會運行一個時間片的時間長度,當時間片到期之后,操作系統將這個線程調度出CPU,將另外一個線程調度進CPU,我們通常稱這種操作為上下文切換。

  在每一次的上下文切換時,Windows將執行下面的步驟:

  • 將當前的CPU寄存器的值保存到當前運行的線程數據結構中,即其中的線程核心對象中。
  • 選中下一個准備運行的線程,如果這個線程處於不同的進程中,那么,還必須首先切換虛擬地址空間。
  • 加載准備運行線程的CPU寄存器狀態到CPU中。

  公共語言運行時CLR(Common Language Runtime)是.Net程序運行的環境,它負責資源管理,並保證應用和底層操作系統之間必要的分離。

  在.Net環境下,CLR中的線程需要通過操作系統的線程完成實際的工作,目前情況下,.Net直接將CLR中的線程映射到操作系統的線程進行處理和調度,所以,我們每創建一個線程將會消耗1M以上的內存空間。但未來CLR中的線程並不一定與操作系統中的線程完全對應。通過創建CLR環境下的邏輯線程,我們可能創建更加節省資源的線程,使得大量的CLR線程可以工作在少量的操作系統線程之上。

 

1. System.Threading.Thread類

System.Threading.Thread是用於控制線程的基礎類,通過Thread可以控制當前應用程序域中線程的創建、掛起、停止、銷毀。

它包括以下常用公共屬性:

屬性名稱 說明
CurrentContext 獲取線程正在其中執行的當前上下文。
CurrentThread 獲取當前正在運行的線程。
ExecutionContext 獲取一個 ExecutionContext 對象,該對象包含有關當前線程的各種上下文的信息。
IsAlive 獲取一個值,該值指示當前線程的執行狀態。
IsBackground 獲取或設置一個值,該值指示某個線程是否為后台線程。
IsThreadPoolThread 獲取一個值,該值指示線程是否屬於托管線程池。
ManagedThreadId 獲取當前托管線程的唯一標識符。
Name 獲取或設置線程的名稱。
Priority 獲取或設置一個值,該值指示線程的調度優先級。
ThreadState 獲取一個值,該值包含當前線程的狀態。

 常用屬性示例:

using System;
using System.Threading;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //新建3個線程並設定各自的優先級
            Thread t1 = new Thread(Run);
            t1.Priority = ThreadPriority.Normal;
            t1.Start();

            Console.ReadKey();
        }

        public static void Run()
        {
            Thread t1 = Thread.CurrentThread;   //靜態屬性,獲取當前執行這行代碼的線程
            Console.WriteLine("我的優先級是:" + t1.Priority);
            Console.WriteLine("我是否還在執行:" + t1.IsAlive);
            Console.WriteLine("是否是后台線程:" + t1.IsBackground);
            Console.WriteLine("是否是線程池線程:" + t1.IsThreadPoolThread);
            Console.WriteLine("線程唯一標識符:" + t1.ManagedThreadId);
            Console.WriteLine("我的名稱是:" + t1.Name);
            Console.WriteLine("我的狀態是:" + t1.ThreadState);
        }
    }
}
View Code

 

2. 線程的標識符

ManagedThreadId是確認線程的唯一標識符,程序在大部分情況下都是通過Thread.ManagedThreadId來辨別線程的。而Name是一個可變值,在默認時候,Name為一個空值 Null,開發人員可以通過程序設置線程的名稱,但這只是一個輔助功能。

 

3. 線程的優先級別

.NET為線程設置了Priority屬性來定義線程執行的優先級別,里面包含5個選項,其中Normal是默認值。除非系統有特殊要求,否則不應該隨便設置線程的優先級別。

成員名稱 說明
Lowest 可以將 Thread 安排在具有任何其他優先級的線程之后。
BelowNormal 可以將 Thread 安排在具有 Normal 優先級的線程之后,在具有 Lowest 優先級的線程之前。
Normal 默認選擇。可以將 Thread 安排在具有 AboveNormal 優先級的線程之后,在具有BelowNormal 優先級的線程之前。
AboveNormal 可以將 Thread 安排在具有 Highest 優先級的線程之后,在具有 Normal 優先級的線程之前。
Highest 可以將 Thread 安排在具有任何其他優先級的線程之前。

 優先級的示例:

using System;
using System.Threading;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //新建3個線程並設定各自的優先級
            Thread t1 = new Thread(Run);
            t1.Priority = ThreadPriority.Lowest;
            Thread t2 = new Thread(Run);
            t2.Priority = ThreadPriority.Normal;
            Thread t3 = new Thread(Run);
            t3.Priority = ThreadPriority.Highest;
            //由低到高優先級的順序依次調用
            t1.Start();
            t2.Start();
            t3.Start();
            Console.ReadKey();
        }

        public static void Run()
        {
            Console.WriteLine("我的優先級是:" + Thread.CurrentThread.Priority);
        }
    }
}
View Code

4. 線程的狀態

通過ThreadState可以檢測線程是處於Unstarted、Sleeping、Running 等等狀態,它比 IsAlive 屬性能提供更多的特定信息。

前面說過,一個應用程序域中可能包括多個上下文,而通過CurrentContext可以獲取線程當前的上下文。

CurrentThread是最常用的一個屬性,它是用於獲取當前運行的線程。

 

5. System.Threading.Thread的方法

Thread 中包括了多個方法來控制線程的創建、掛起、停止、銷毀,以后來的例子中會經常使用。

方法名稱 說明
Abort()     終止本線程。
GetDomain() 返回當前線程正在其中運行的當前域。
GetDomainId() 返回當前線程正在其中運行的當前域Id。
Interrupt() 中斷處於 WaitSleepJoin 線程狀態的線程。
Join() 已重載。 阻塞調用線程,直到某個線程終止時為止。
Resume() 繼續運行已掛起的線程。
Start()   執行本線程。
Suspend() 掛起當前線程,如果當前線程已屬於掛起狀態則此不起作用
Sleep()   把正在運行的線程掛起一段時間。

6. 開發實例

 前台線程與后台線程的區別

  我們看到上面有個屬性叫后台線程,非后台線程就叫前台線程吧,Thread.Start()啟動的線程默認為前台線程,啟動程序時創建的主線程一定是前台線程。應用程序與必須等到所有的前台線程執行完畢才會卸載。而當IsBackground設置為true時,就是后台線程了,當主線程執行完畢后就直接卸載,不再理會后台線程是否執行完畢。

  前台與后台線程的設置必須在線程啟動之前進行設置,線程啟動之后就不能設置了。

Thread創建的線程是前台線程,線程池中的是后台線程。

1、啟動線程(.Start)
  1.1、無參數的調用
   Thread t = new Thread(Action);
   t.Start();
  1.2、帶參數的調用方式一
   Thread t = new Thread(Action<>);
   t.Start(參數);
  1.3、帶參數的調用方式二
   Thread t = new Thread(() => Action<>(參數));
   t.Start();
  1.4、如果遇到需要返回值,就可以考慮用異步;
   public delegate string MethodCaller(string name);//定義個代理 
   MethodCaller mc = new MethodCaller(GetName); 
   string name = "my name";//輸入參數 
   IAsyncResult result = mc.BeginInvoke(name,null, null); 
   string myname = mc.EndInvoke(result);//用於接收返回值 
 
   public string GetName(string name)    // 函數
   {
      return name;
   }
using System;
using System.Threading;

namespace ConsoleApp1
{
    public delegate string MethodCaller(string name);//定義個代理 

    class Program
    {
        static void CountNumbers()
        {
            int _iterations = 3;
            for (int i = 1; i <= _iterations; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
            }
        }

        static void Count(object iterations)
        {
            CountNumbers((int)iterations);
        }

        static void CountNumbers(int iterations)
        {
            for (int i = 1; i <= iterations; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
            }
        }

        static void PrintNumber(int number)
        {
            Console.WriteLine(number);
        }

        static string GetName(string name)    // 函數
        {
            int _iterations = 5;
            for (int i = 1; i <= _iterations; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
            }
            return name;
        }

        static void Main(string[] args)
        {
            //無參數的調用
            var threadOne = new Thread(CountNumbers);
            threadOne.Name = "ThreadOne";
            threadOne.Start();
            threadOne.Join();
            Console.WriteLine("--------------------------");

            //帶參數的調用方式一
            var threadTwo = new Thread(Count);
            threadTwo.Name = "ThreadTwo";
            threadTwo.Start(3);
            threadTwo.Join();
            Console.WriteLine("--------------------------");

            //帶參數的調用方式二
            var threadThree = new Thread(() => CountNumbers(4));
            threadThree.Name = "ThreadThree";
            threadThree.Start();
            threadThree.Join();
            Console.WriteLine("--------------------------");

            //注意在多個lambda表達式中使用香港的變量,它們會共享該變量 這里兩個線程的參數都是20
            int i = 10;
            var threadFour = new Thread(() => PrintNumber(i));
            i = 20;
            var threadFive = new Thread(() => PrintNumber(i));
            threadFour.Start();
            threadFive.Start();
            Console.WriteLine("--------------------------");

            MethodCaller mc = new MethodCaller(GetName);
            string name = "my name";//輸入參數 
            IAsyncResult result = mc.BeginInvoke(name, null, null);
            Console.WriteLine("繼續主線程的業務");
            string myname = mc.EndInvoke(result);//用於接收返回值 
            Console.WriteLine(string.Format("result={0}", myname));
        }
    }
}
View Code

2、等待線程結束,會阻塞當前主線程(.Join)

using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting program...");
            Thread t = new Thread(PrintNumbersWithDelay);
            t.Start();
            t.Join();
            Console.WriteLine("Thread completed");
        }

        static void PrintNumbersWithDelay()
        {
            Console.WriteLine("Starting...");
            for (int i = 1; i <= 5; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.WriteLine(i);
            }
        }
    }
}
View Code

3、線程中異常的處理只能在線程調用中的函數里面去處理,在外面是接收不到線程中的報錯的

using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var t = new Thread(FaultyThread);
            t.Start();
            t.Join();

            //try
            //{
            //    t = new Thread(BadFaultyThread);
            //    t.Start();
            //}
            //catch (Exception ex)
            //{
            //    //這里捕獲不了線程中的異常
            //    Console.WriteLine("We won't get here!");
            //}
        }

        static void BadFaultyThread()
        {
            Console.WriteLine("Starting a Badfaulty thread...");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            throw new Exception("Boom!");
        }

        static void FaultyThread()
        {
            try
            {
                Console.WriteLine("Starting a faulty thread...");
                Thread.Sleep(TimeSpan.FromSeconds(1));
                throw new Exception("Boom!");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception handled: {0}", ex.Message);
            }
        }
    }
}
View Code

4、設置線程優先級(.Priority)

using System;
using System.Diagnostics;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Current thread priority: {0}", Thread.CurrentThread.Priority);
            Console.WriteLine("Running on all cores available");
            RunThreads();
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine("Running on a single core");
            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
            RunThreads();
            Console.ReadLine();
        }

        static void RunThreads()
        {
            var sample = new ThreadSample();

            var threadOne = new Thread(sample.CountNumbers);
            threadOne.Name = "ThreadOne";
            var threadTwo = new Thread(sample.CountNumbers);
            threadTwo.Name = "ThreadTwo";

            threadOne.Priority = ThreadPriority.Lowest;
            threadTwo.Priority = ThreadPriority.Highest;
            threadOne.Start();
            threadTwo.Start();

            Thread.Sleep(TimeSpan.FromSeconds(2));
            sample.Stop();
        }

        class ThreadSample
        {
            private bool _isStopped = false;

            public void Stop()
            {
                _isStopped = true;
            }

            public void CountNumbers()
            {
                long counter = 0;

                while (!_isStopped)
                {
                    counter++;
                }

                Console.WriteLine("{0} with {1,11} priority " +
                            "has a count = {2,13}", Thread.CurrentThread.Name,
                            Thread.CurrentThread.Priority,
                            counter.ToString("N0"));
            }
        }
    }
}
View Code

 

 


免責聲明!

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



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