1.線程基礎(chǔ) -《多線程編程實(shí)戰(zhàn)》
線程的生命周期濒翻,包括創(chuàng)建線程、掛起線程、線程等待有送,以及中止線程淌喻。
1.2 創(chuàng)建線程
class Program
{
static void Main(string[] args)
{
Thread t = new Thread(PrintNumbers);
t.Start();
PrintNumbers();
Console.Read();
}
static void PrintNumbers()
{
Console.WriteLine("開始...");
for (int i = 1; i < 50; i++)
{
Console.WriteLine(i);
}
}
}
正在執(zhí)行中的程序?qū)嵗杀环Q為一個(gè)進(jìn)程。進(jìn)程由一個(gè)或多個(gè)線程組成雀摘。這意味著當(dāng)運(yùn)行程序時(shí)裸删,始終有一個(gè)執(zhí)行程序代碼的是主線程。
結(jié)果兩組范圍為1到50的數(shù)字會(huì)隨機(jī)交叉輸出阵赠,說明 PrintNumbers 方法同時(shí)運(yùn)行在主線程和另一個(gè)線程中涯塔。
1.3 暫停線程(Thread.Sleep)
本節(jié)展示讓一個(gè)線程等待一段時(shí)間,而不用消耗操作系統(tǒng)資源豌注。
static void Main(string[] args)
{
Thread t = new Thread(PrintNumbersWithDelay);
t.Start();
PrintNumbers();
Console.Read();
}
static void PrintNumbers()
{
Console.WriteLine("PrintNumbers開始...");
for (int i = 1; i < 10; i++)
{
Console.WriteLine(i);
}
}
static void PrintNumbersWithDelay()
{
Console.WriteLine("PrintNumbersWithDelay開始...");
for (int i = 1; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("多線程:" + i);
}
}
當(dāng)程序運(yùn)行時(shí)伤塌,會(huì)創(chuàng)建一個(gè)線程執(zhí)行 PrintNumbersWithDelay 方法。然后立即執(zhí)行 PrintNumbers 轧铁。PrintNumbersWithDelay 加入了
Thread.Sleep
(休眠)2秒每聪。當(dāng)線程休眠狀態(tài)時(shí),會(huì)盡可能少的 CPU 時(shí)間齿风。結(jié)果發(fā)現(xiàn) PrintNumbers 先執(zhí)行完药薯。
1.4 線程等待(Thread.Join)#
本節(jié)展示如何讓程序等待另一個(gè)線程中的計(jì)算完成,然后在代碼中使用該線程的計(jì)算結(jié)果救斑。使用 Thread.Sleep 行不通童本,因?yàn)椴⒉恢缊?zhí)行計(jì)算需要花費(fèi)的具體時(shí)間。
static void Main(string[] args)
{
Console.WriteLine("主線程開始...");
Thread t = new Thread(PrintNumbersWithDelay);
t.Start();
t.Join();
Console.WriteLine("Thread completed");
Console.Read();
}
static void PrintNumbersWithDelay()
{
Console.WriteLine("PrintNumbersWithDelay開始...");
for (int i = 1; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("多線程:" + i);
}
}
當(dāng)程序啟動(dòng)一個(gè)耗時(shí)較長的線程來打印數(shù)字脸候,打印每個(gè)數(shù)字前要等待2秒穷娱。但我們主程序調(diào)用了
t.Join
方法,該方法允許我們等待指導(dǎo)線程 t 完成运沦。當(dāng)線程完成時(shí)泵额,主程序會(huì)繼續(xù)運(yùn)行。
借助該技術(shù)可以實(shí)現(xiàn)兩個(gè)線程間的同步執(zhí)行步驟携添。第一個(gè)線程會(huì)等待另一個(gè)線程完成后再繼續(xù)執(zhí)行嫁盲。第一個(gè)線程等待時(shí),是處于阻塞狀態(tài)烈掠。
1.5 終止線程(Thread.Abort)#
static void Main(string[] args)
{
Console.WriteLine("開始程序..");
Thread t = new Thread(PrintNumbersWithDelay);
t.Start();
Thread.Sleep(TimeSpan.FromSeconds(6));
t.Abort();
Console.WriteLine("一個(gè)線程終止了~~");
t = new Thread(PrintNumbers);
t.Start();
PrintNumbers();
Console.Read();
}
static void PrintNumbersWithDelay()
{
Console.WriteLine("PrintNumbersWithDelay開始...");
for (int i = 1; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("多線程:" + i +"羞秤,當(dāng)前線程:"+ Thread.CurrentThread.ManagedThreadId);
}
}
static void PrintNumbers()
{
Console.WriteLine("PrintNumbers 開始...");
for (int i = 1; i < 10; i++)
{
Console.WriteLine(i + ",當(dāng)前線程:" + Thread.CurrentThread.ManagedThreadId);
}
}
當(dāng)主程序和單獨(dú)的數(shù)字打印線程運(yùn)行時(shí)左敌,等待6秒調(diào)用
t.Abort
方法瘾蛋。這給線程注入 ThreadAbortException 方法,導(dǎo)致線程被終止矫限。使用該技術(shù)不一定能終止線程瘦黑。目標(biāo)線程可以通過處理該異常并調(diào)用
Thread.ResetAbort
方法來拒絕被終止京革。因此不推薦使用Abort
方法關(guān)閉線程⌒页猓可以使用其他方法匹摇,比如提供一個(gè)CancellationToken
方法來取消線程執(zhí)行。
1.6 檢測線程狀態(tài)(Thread.CurrentThread)#
public enum ThreadState
{
Running = 0, //線程已啟動(dòng)甲葬,它未被阻塞廊勃,并且沒有掛起的
StopRequested = 1, //正在請求線程停止。 這僅用于內(nèi)部
SuspendRequested = 2, //正在請求線程掛起
Background = 4, //線程正作為后臺線程執(zhí)行(相對于前臺線程而言)通過設(shè)置Thread.IsBackground來控制
Unstarted = 8, //尚未對線程調(diào)用 Thread.Start() 方法
Stopped = 16, //線程已停止
WaitSleepJoin = 32, //線程已被阻止经窖。Thread.Sleep 或 Thread.Join()坡垫、請求鎖定或等待線程同步對象
Suspended = 64, //線程已掛起
AbortRequested = 128, //已對線程調(diào)用了 Thread.Abort 方法,但線程尚未收到試圖終止它的掛起的
Aborted = 256, //程狀態(tài)包括ThreadState.AbortRequested并且該線程現(xiàn)在已死画侣,但其狀態(tài)尚未更改為ThreadState.Stopped
}
下面看示例:
static void Main(string[] args)
{
Console.WriteLine("開始程序冰悠。。");
Thread t = new Thread(PrintNumbersWithStatus);
Thread t2 = new Thread(DoNothing);
Console.WriteLine("t的線程狀態(tài):" + t.ThreadState.ToString() + "配乱,t線程:" + t.ManagedThreadId);
t2.Start();
t.Start();
for (int i = 1; i < 30; i++)
{
Console.WriteLine("t的線程狀態(tài):" + t.ThreadState.ToString() + "溉卓,t線程:" + t.ManagedThreadId);
}
Thread.Sleep(TimeSpan.FromSeconds(6));
t.Abort();
Console.WriteLine("一個(gè)線程終止了~~");
Console.WriteLine("t的線程狀態(tài):" + t.ThreadState.ToString() + ",t線程:" + t.ManagedThreadId);
Console.WriteLine("t2的線程狀態(tài):" + t2.ThreadState.ToString() + "搬泥,t2線程:" + t2.ManagedThreadId);
Console.Read();
}
static void DoNothing()
{
Console.WriteLine("DoNothing開始...");
Thread.Sleep(TimeSpan.FromSeconds(2));
}
static void PrintNumbersWithStatus()
{
Console.WriteLine("PrintNumbersWithStatus開始...");
Console.WriteLine("當(dāng)前線程狀態(tài):" + Thread.CurrentThread.ThreadState.ToString() + "桑寨,當(dāng)前線程:" + Thread.CurrentThread.ManagedThreadId);
for (int i = 1; i < 10; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("多線程:" + i + ",當(dāng)前線程:" + Thread.CurrentThread.ManagedThreadId);
}
}