關(guān)于C#線程的全面解析
線程的作用和意義
線程 被定義為程序的執(zhí)行路徑。每個(gè)線程都定義了一個(gè)獨(dú)特的控制流。如果您的應(yīng)用程序涉及到復(fù)雜的和耗時(shí)的操作,那么設(shè)置不同的線程執(zhí)行路徑往往是有益的,每個(gè)線程執(zhí)行特定的工作。
線程是輕量級進(jìn)程。一個(gè)使用線程的常見實(shí)例是現(xiàn)代操作系統(tǒng)中并行編程的實(shí)現(xiàn)。使用線程節(jié)省了 CPU 周期的浪費(fèi),同時(shí)提高了應(yīng)用程序的效率。
到目前為止我們編寫的程序是一個(gè)單線程作為應(yīng)用程序的運(yùn)行實(shí)例的單一的過程運(yùn)行的。但是,這樣子應(yīng)用程序同時(shí)只能執(zhí)行一個(gè)任務(wù)。為了同時(shí)執(zhí)行多個(gè)任務(wù),它可以被劃分為更小的線程。
線程生命周期
線程生命周期開始于 System.Threading.Thread 類的對象被創(chuàng)建時(shí),結(jié)束于線程被終止或完成執(zhí)行時(shí)。
下面列出了線程生命周期中的各種狀態(tài):
- 未啟動(dòng)狀態(tài):當(dāng)線程實(shí)例被創(chuàng)建但 Start 方法未被調(diào)用時(shí)的狀況。
- 就緒狀態(tài):當(dāng)線程準(zhǔn)備好運(yùn)行并等待 CPU 周期時(shí)的狀況。
- 不可運(yùn)行狀態(tài):下面的幾種情況下線程是不可運(yùn)行的:
- 已經(jīng)調(diào)用 Sleep 方法
- 已經(jīng)調(diào)用 Wait 方法
- 通過 I/O 操作阻塞
- 死亡狀態(tài):當(dāng)線程已完成執(zhí)行或已中止時(shí)的狀況
C#創(chuàng)建線程
在C# 語言中使用線程時(shí)首先需要?jiǎng)?chuàng)建線程,在使用 Thread 類的構(gòu)造方法創(chuàng)建其實(shí)例時(shí),需要用到 ThreadStart 委托或者 ParameterizedThreadStart 委托創(chuàng)建 Thread 類的實(shí)例。ThreadStart 委托只能用于無返回值、無參數(shù)的方法,而ParameterizedThreadStart 委托則可以用于帶參數(shù)的方法。
ThreadStar的方式創(chuàng)建
例子:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
//線程函數(shù)
public static void CallToChildThread()
{
Console.WriteLine("Child thread starts");
}
static void Main(string[] args)
{
//創(chuàng)建ThreadStart的委托實(shí)例
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
//創(chuàng)建Thread類的實(shí)例
Thread childThread = new Thread(childref);
childThread.Start(); //開始一個(gè)線程
Console.ReadKey();
}
}
}
運(yùn)行結(jié)果:

ParameterizedThreadStart
例子:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class Program
{
static void Main(string[] args)
{
//創(chuàng)建一個(gè)線程委托對象
ParameterizedThreadStart pts = new ParameterizedThreadStart(PrintEven);
Console.WriteLine("In Main: Creating the Child thread");
// 創(chuàng)建一個(gè)線程對象
Thread childThread = new Thread(pts);
childThread.Start(10);
Console.ReadKey();
}
//線程跑的函數(shù)
//打印0~n中的偶數(shù)
private static void PrintEven(Object n)
{
Console.WriteLine("Child thread started");
for(int i=0; i<=(int)n; i+=2) //類型轉(zhuǎn)換
{
Console.WriteLine(i);
}
}
}
}
運(yùn)行結(jié)果:

C#讓線程休眠一會(huì)
using System;
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
Console.WriteLine("Child thread starts");
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)
{
//創(chuàng)建一個(gè)線程的委托
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
//創(chuàng)建線程的實(shí)例
Thread childThread = new Thread(childref);
childThread.Start();
Console.ReadKey();
}
}
}
運(yùn)行結(jié)果:最后一行是5s后才打印出來的

C#銷毀線程
/*
銷毀線程
*/
using System;
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
//委托函數(shù)
public static void CallToChildThread()
{
try//引起異常的語句
{
Console.WriteLine("Child thread starts");
for(int counter = 0; counter <= 10; counter++)
{
Thread.Sleep(500);
Console.WriteLine(counter);
}
Console.WriteLine("Child Thread Completed");
}
catch(ThreadAbortException e)//錯(cuò)誤處理代碼
{
Console.WriteLine("Thread Abort Exception");
}
finally //執(zhí)行的語句
{
Console.WriteLine("Could't catch the Thread Exception");
}
}
static void Main(string[] args)
{
//創(chuàng)建一個(gè)線程的委托實(shí)例
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
//創(chuàng)建一個(gè)線程對象
Thread childThread = new Thread(childref);
childThread.Start();
//主線程休眠
Thread.Sleep(2000);
Console.WriteLine("In Main:Aborting the Child thread");
//在調(diào)用此方法的線程上引發(fā)ThreadAbortException,以開始終止此線程的過程。
//調(diào)用此方法通常會(huì)終止線程
childThread.Abort();
Console.ReadKey();
}
}
}
運(yùn)行結(jié)果:

C#線程優(yōu)先級
在C#中線程的優(yōu)先級使用線程的Priority屬性設(shè)置即可,默認(rèn)的優(yōu)先級是Normal。在設(shè)置優(yōu)先級后,優(yōu)先級高的線程將優(yōu)先執(zhí)行。優(yōu)先級的值通關(guān)ThreadPriority枚舉類型來設(shè)置,從低到高分別為Lowest 、BelowNormal、Normal、 AboveNormal、 Highest。
例子:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class Program
{
//奇數(shù)
public static void PrintOdd()
{
Console.WriteLine("List of odd numbers:");
for (int i = 1; i <= 100; i += 2)
{
Console.Write(i + " ");
}
Console.WriteLine();
}
//偶數(shù)
public static void PrintEven()
{
Console.WriteLine("List of even numbers: ");
for(int i = 0; i<=100; i+=2)
{
Console.Write(i + " ");
}
Console.WriteLine();
}
static void Main(string[] args)
{
//創(chuàng)建線程的委托1
ThreadStart childref1 = new ThreadStart(PrintEven);
Console.WriteLine("In Main: Creating the Child1 thread");
//創(chuàng)建線程1的實(shí)例
Thread childThread1 = new Thread(childref1);
//設(shè)置打印偶數(shù)優(yōu)先級為最低
childThread1.Priority = ThreadPriority.Lowest;
//創(chuàng)建線程的委托2
ThreadStart childref2 = new ThreadStart(PrintOdd);
Console.WriteLine("In Main: Creating the Child2 thread");
//創(chuàng)建線程2的實(shí)例
Thread childThread2 = new Thread(childref2);
//設(shè)置打印奇數(shù)優(yōu)先級為最高
childThread2.Priority = ThreadPriority.Highest;
childThread1.Start();//偶數(shù) 低
childThread2.Start();//奇數(shù) 高
Console.ReadKey();
}
}
}
運(yùn)行的結(jié)果:
第一次運(yùn)行:

第二次:

第三次:

第四次:

小結(jié):
從上面的運(yùn)行效果可以看出,由于輸岀奇數(shù)的線程的優(yōu)先級高于輸出偶數(shù)的線程,所以在輸出結(jié)果中優(yōu)先輸出奇數(shù)的次數(shù)會(huì)更多。
此外,每次輸出的結(jié)果也不是固定的。通過優(yōu)先級是不能控制線程中的先后執(zhí)行順序的,只能是優(yōu)先級高的線程優(yōu)先執(zhí)行的次數(shù)多而已。
線程狀態(tài)控制的方法包括暫停線程 (Sleep)、中斷線程 (Interrupt)、掛起線程 (Suspend)、喚醒線程 (Resume)、終止線程 (Abort)。
lock:給線程加鎖,保證線程同步
sleep 方法能控制線程的暫停時(shí)間,從而改變多個(gè)線程之間的先后順序,但每次調(diào)用線程的結(jié)果是隨機(jī)的。線程同步的方法是將線程資源共享,允許控制每次執(zhí)行一個(gè)線程,并交替執(zhí)行每個(gè)線程。在 C# 語言中實(shí)現(xiàn)線程同步可以使用 lock 關(guān)鍵字和 Monitor 類、Mutex 類來解決。對于線程同步操作最簡單的一種方式就是使用 lock 關(guān)鍵字,通過 lock 關(guān)鍵字能保證加鎖的線程只有在執(zhí)行完成后才能執(zhí)行其他線程。
lock的語法如下
lock(object)
{
//臨界區(qū)代碼
}
這里 lock 后面通常是一個(gè) Object 類型的值,也可以使用 this 關(guān)鍵字來表示。
最好是在 lock 中使用私有的非靜態(tài)或負(fù)變量或私有的靜態(tài)成員變量,即使用 Private 或 Private static 修飾的成員。
例如:
private Object obj = new Object();
lock (obj)
{
//臨界區(qū)代碼
}
一個(gè)更具體的實(shí)例
using System;
using System.Threading;
namespace MultithreadingApplication
{
class Program
{
//打印偶數(shù)
public void PrintEven()
{
//lock上鎖保證執(zhí)行完該線程才跑其他線程
lock(this)
{
for(int i=0; i<=10; i+=2)
{
//獲取當(dāng)前線程的名字
Console.WriteLine(Thread.CurrentThread.Name + "--" + i);
}
}
}
//打印奇數(shù)
public void PrintOdd()
{
lock (this)
{
for (int i = 1; i <= 10; i += 2)
{
Console.WriteLine(Thread.CurrentThread.Name + "--" + i);
}
}
}
static void Main(string[] args)
{
//因?yàn)橄旅嬉玫絧rogram類中的非靜態(tài)函數(shù),所以先創(chuàng)建該類對象
Program program = new Program();
//創(chuàng)建線程1的托管
ThreadStart ts1 = new ThreadStart(program.PrintOdd);
//創(chuàng)建線程1
Thread t1 = new Thread(ts1);
t1.Name = "打印奇數(shù)的線程";
//跑線程1
t1.Start();
ThreadStart ts2 = new ThreadStart(program.PrintEven);
Thread t2 = new Thread(ts2);
t2.Name = "打印偶數(shù)的線程";
t2.Start();
}
}
}
運(yùn)行結(jié)果:

Monitor:鎖定資源
和lock用法本質(zhì)是一樣的,使用Monitor類鎖定資源的語法如下:
Monitor.Enter(object);
try
{
//臨界區(qū)代碼
}
finally
{
Monitor.Exit(object);
}
這里的object與lock中的object一樣。
具體例子
sing System;
using System.Threading;
namespace MultithreadingApplication
{
class Program
{
public void PrintEven()
{
//在指定對象上獲取排它鎖
Monitor.Enter(this);
try//臨界區(qū)代碼
{
for(int i=0; i<=10; i+=2)
{
Console.WriteLine(Thread.CurrentThread.Name + "--" + i);
}
}
finally
{
//釋放指定對象的排它鎖
Monitor.Exit(this);
}
}
public void PrintOdd()
{
Monitor.Enter(this);
try
{
for(int i=1; i<=10; i+=2)
{
Console.WriteLine(Thread.CurrentThread.Name + "--" + i);
}
}
finally
{
Monitor.Exit(this);
}
}
static void Main(string[] args)
{
//下面創(chuàng)建委托對象時(shí)調(diào)用的是Program類的非靜態(tài)方法,
//所先創(chuàng)建一個(gè)program對象
Program program = new Program();
//實(shí)例化一個(gè)委托
ThreadStart ts1 = new ThreadStart(program.PrintOdd);
//創(chuàng)建一個(gè)線程
Thread t1 = new Thread(ts1);
//給線程名字賦值
t1.Name = "打印奇數(shù)的線程";
//開跑線程
t1.Start();
ThreadStart ts2 = new ThreadStart(program.PrintEven);
Thread t2 = new Thread(ts2);
t2.Name = "打印偶數(shù)的線程";
t2.Start();
}
}
}
運(yùn)行結(jié)果:

Monitor 類的用法雖然比 lock 關(guān)鍵字復(fù)雜,但其能添加等待獲得鎖定的超時(shí)值,這樣就不會(huì)無限期等待獲得對象鎖。使用 TryEnter() 方法可以給它傳送一個(gè)超時(shí)值,決定等待獲得對象鎖的最長時(shí)間。
使用 TryEnter() 方法設(shè)置獲得對象鎖的時(shí)間的語法如下:
Monitor.TryEnter(object, 毫秒數(shù) );
Mutex:互斥鎖
Mutex類也是用于線程同步操作的類,當(dāng)多個(gè)線程同時(shí)訪問一個(gè)資源識保證只有一個(gè)線程訪問資源。在Mutex類中,WaitOne()方法用于等待資源被釋放,ReleaseMutex()方法用于釋放資源。 WaitOne()方法在等待ReleMutex()方法執(zhí)行后才會(huì)結(jié)束。
例子:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class Program
{
//創(chuàng)建一個(gè)鎖對象
private static Mutex mutex = new Mutex();
public static void PakingSpace(object num)
{
if(mutex.WaitOne())//等待釋放資源,當(dāng)前資源沒調(diào)用時(shí)為true
{
try
{
Console.WriteLine("車牌號{0}的車駛?cè)耄?, num);
Thread.Sleep(1000);//線程休眠一秒
}
finally
{
Console.WriteLine("車牌號{0}的車離開!", num);
mutex.ReleaseMutex(); //釋放鎖資源
}
}
}
static void Main(string[] args)
{
//創(chuàng)建一個(gè)委托帶參數(shù)的
ParameterizedThreadStart ts = new ParameterizedThreadStart(PakingSpace);
//創(chuàng)建一個(gè)線程
Thread t1 = new Thread(ts);
t1.Start("A123456");
Thread t2 = new Thread(ts);
t2.Start("B00000");
Console.ReadKey();
}
}
}
運(yùn)行結(jié)果:

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C#使用NPOI實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出功能
這篇文章主要為大家詳細(xì)介紹了C#使用NPOI實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
C#實(shí)現(xiàn)添加多行文本水印到Word文檔
一般情況下,在Word中添加文字水印僅支持添加一個(gè)文本字樣的水印,由于對不同文檔的設(shè)計(jì)要求,需要在Word文檔中添加平鋪水印效果。本文將介紹如何來實(shí)現(xiàn)該水印效果的方法,感興趣的可以了解一下2022-07-07
C#使用自定義算法對數(shù)組進(jìn)行反轉(zhuǎn)操作的方法
這篇文章主要介紹了C#使用自定義算法對數(shù)組進(jìn)行反轉(zhuǎn)操作的方法,涉及C#針對數(shù)組操作的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
C#實(shí)現(xiàn)的pdf生成圖片文字水印類實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)的pdf生成圖片文字水印類,結(jié)合完整實(shí)例形式分析了C#針對pdf文件的創(chuàng)建、添加文字、水印等相關(guān)操作技巧,需要的朋友可以參考下2017-09-09
C#基于DBContext(EF)實(shí)現(xiàn)通用增刪改查的REST方法實(shí)例
這篇文章主要介紹了C#基于DBContext(EF)實(shí)現(xiàn)通用增刪改查的REST方法實(shí)例,是C#程序設(shè)計(jì)中非常實(shí)用的技巧,需要的朋友可以參考下2014-10-10

