Latest web development tutorials

C# 多線程

線程被定義為程序的執行路徑。每個線程都定義了一個獨特的控制流。 如果您的應用程序涉及到復雜的和耗時的操作,那麼設置不同的線程執行路徑往往是有益的,每個線程執行特定的工作。

線程是輕量級進程 。 一個使用線程的常見實例是現代操作系統中並行編程的實現。 使用線程節省了CPU 週期的浪費,同時提高了應用程序的效率。

到目前為止我們編寫的程序是一個單線程作為應用程序的運行實例的單一的過程運行的。 但是,這樣子應用程序同時只能執行一個任務。 為了同時執行多個任務,它可以被劃分為更小的線程。

線程生命週期

線程生命週期開始於System.Threading.Thread 類的對像被創建時,結束於線程被終止或完成執行時。

下面列出了線程生命週期中的各種狀態:

  • 未啟動狀態 :當線程實例被創建但Start方法未被調用時的狀況。
  • 就緒狀態 :當線程準備好運行並等待CPU週期時的狀況。
  • 不可運行狀態 :下面的幾種情況下線程是不可運行的:

    • 已經調用Sleep 方法
    • 已經調用Wait 方法
    • 通過I/O 操作阻塞
  • 死亡狀態 :當線程已完成執行或已中止時的狀況。

主線程

在C#中,System.Threading.Thread類用於線程的工作。它允許創建並訪問多線程應用程序中的單個線程。 進程中第一個被執行的線程稱為主線程 。

當C# 程序開始執行時,主線程自動創建。 使用Thread類創建的線程被主線程的子線程調用。 您可以使用Thread類的CurrentThread屬性訪問線程。

下面的程序演示了主線程的執行:

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class MainThreadProgram
    {
        static void Main(string[] args)
        {
            Thread th = Thread.CurrentThread;
            th.Name = "MainThread";
            Console.WriteLine("This is {0}", th.Name);
            Console.ReadKey();
        }
    }
}

當上面的代碼被編譯和執行時,它會產生下列結果:

This is MainThread

Thread 類常用的屬性和方法

下表列出了Thread類的一些常用的屬性

属性描述
CurrentContext获取线程正在其中执行的当前上下文。
CurrentCulture获取或设置当前线程的区域性。
CurrentPrinciple获取或设置线程的当前负责人(对基于角色的安全性而言)。
CurrentThread获取当前正在运行的线程。
CurrentUICulture获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源。
ExecutionContext获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。
IsAlive获取一个值,该值指示当前线程的执行状态。
IsBackground获取或设置一个值,该值指示某个线程是否为后台线程。
IsThreadPoolThread获取一个值,该值指示线程是否属于托管线程池。
ManagedThreadId获取当前托管线程的唯一标识符。
Name获取或设置线程的名称。
Priority获取或设置一个值,该值指示线程的调度优先级。
ThreadState获取一个值,该值包含当前线程的状态。

下表列出了Thread類的一些常用的方法

序号方法名 & 描述
1public void Abort()
在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。调用此方法通常会终止线程。
2public static LocalDataStoreSlot AllocateDataSlot()
在所有的线程上分配未命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
3public static LocalDataStoreSlot AllocateNamedDataSlot( string name)
在所有线程上分配已命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
4public static void BeginCriticalRegion()
通知主机执行将要进入一个代码区域,在该代码区域内线程中止或未经处理的异常的影响可能会危害应用程序域中的其他任务。
5public static void BeginThreadAffinity()
通知主机托管代码将要执行依赖于当前物理操作系统线程的标识的指令。
6public static void EndCriticalRegion()
通知主机执行将要进入一个代码区域,在该代码区域内线程中止或未经处理的异常仅影响当前任务。
7public static void EndThreadAffinity()
通知主机托管代码已执行完依赖于当前物理操作系统线程的标识的指令。
8public static void FreeNamedDataSlot(string name)
为进程中的所有线程消除名称与槽之间的关联。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
9public static Object GetData( LocalDataStoreSlot slot )
在当前线程的当前域中从当前线程上指定的槽中检索值。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
10public static AppDomain GetDomain()
返回当前线程正在其中运行的当前域。
11public static AppDomain GetDomainID()
返回唯一的应用程序域标识符。
12public static LocalDataStoreSlot GetNamedDataSlot( string name )
查找已命名的数据槽。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
13public void Interrupt()
中断处于 WaitSleepJoin 线程状态的线程。
14public void Join()
在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。此方法有不同的重载形式。
15public static void MemoryBarrier()
按如下方式同步内存存取:执行当前线程的处理器在对指令重新排序时,不能采用先执行 MemoryBarrier 调用之后的内存存取,再执行 MemoryBarrier 调用之前的内存存取的方式。
16public static void ResetAbort()
取消为当前线程请求的 Abort。
17public static void SetData( LocalDataStoreSlot slot, Object data )
在当前正在运行的线程上为此线程的当前域在指定槽中设置数据。为了获得更好的性能,请改用以 ThreadStaticAttribute 属性标记的字段。
18public void Start()
开始一个线程。
19public static void Sleep( int millisecondsTimeout )
让线程暂停一段时间。
20public static void SpinWait( int iterations )
导致线程等待由 iterations 参数定义的时间量。
21public static byte VolatileRead( ref byte address )
public static double VolatileRead( ref double address )
public static int VolatileRead( ref int address )
public static Object VolatileRead( ref Object address )

读取字段值。无论处理器的数目或处理器缓存的状态如何,该值都是由计算机的任何处理器写入的最新值。此方法有不同的重载形式。这里只给出了一些形式。
22public static void VolatileWrite( ref byte address, byte value )
public static void VolatileWrite( ref double address, double value )
public static void VolatileWrite( ref int address, int value )
public static void VolatileWrite( ref Object address, Object value )

立即向字段写入一个值,以使该值对计算机中的所有处理器都可见。此方法有不同的重载形式。这里只给出了一些形式。
23public static bool Yield()
导致调用线程执行准备好在当前处理器上运行的另一个线程。由操作系统选择要执行的线程。

創建線程

線程是通過擴展Thread 類創建的。 擴展的Thread類調用Start()方法來開始子線程的執行。

下面的程序演示了這個概念:

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
        }
        
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

當上面的代碼被編譯和執行時,它會產生下列結果:

In Main: Creating the Child thread
Child thread starts

管理線程

Thread 類提供了各種管理線程的方法。

下面的實例演示了sleep()方法的使用,用於在一個特定的時間暫停線程。

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
            // 線程暫停5000 毫秒int sleepfor = 5000; 
            Console.WriteLine("Child Thread Paused for {0} seconds", 
                              sleepfor / 1000);
            Thread.Sleep(sleepfor);
            Console.WriteLine("Child thread resumes");
        }
        
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

當上面的代碼被編譯和執行時,它會產生下列結果:

In Main: Creating the Child thread
Child thread starts
Child Thread Paused for 5 seconds
Child thread resumes

銷毀線程

Abort()方法用於銷毀線程。

通過拋出threadabortexception在運行時中止線程。 這個異常不能被捕獲,如果有finally塊,控制會被送至finally塊。

下面的程序說明了這點:

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            try
            {

                Console.WriteLine("Child thread starts");
                // 計數到10
                for (int counter = 0; counter <= 10; counter++)
                {
                    Thread.Sleep(500);
                    Console.WriteLine(counter);
                }
                Console.WriteLine("Child Thread Completed");

            }
            catch (ThreadAbortException e)
            {
                Console.WriteLine("Thread Abort Exception");
            }
            finally
            {
                Console.WriteLine("Couldn't catch the Thread Exception");
            }

        }
        
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            // 停止主線程一段時間Thread.Sleep(2000);
            // 現在中止子線程Console.WriteLine("In Main: Aborting the Child thread");
            childThread.Abort();
            Console.ReadKey();
        }
    }
}

當上面的代碼被編譯和執行時,它會產生下列結果:

In Main: Creating the Child thread
Child thread starts
0
1
2
In Main: Aborting the Child thread
Thread Abort Exception
Couldn't catch the Thread Exception