c#多線程之線程基礎(chǔ)
一、簡介
1.為了防止一個(gè)應(yīng)用程序控制CPU而導(dǎo)致其他應(yīng)用程序和操作系統(tǒng)本身永遠(yuǎn)被掛起這一可能情況,操作系統(tǒng)不得不使用某種方式將物理計(jì)算分割為一些虛擬的進(jìn)程,并給予每個(gè)執(zhí)行程序一定量的計(jì)算能力。此外操作系統(tǒng)必須始終能夠優(yōu)先訪問CPU,并能調(diào)整不同程序訪問CPU的優(yōu)先級(jí)。線程正式這一慨念的實(shí)現(xiàn)。
2.多線程優(yōu)缺點(diǎn):
多線程優(yōu)點(diǎn):可以同時(shí)執(zhí)行多個(gè)計(jì)算任務(wù),有可能提高計(jì)算機(jī)的處理能力,使得計(jì)算機(jī)每秒能執(zhí)行越來越多的命令
多線程缺點(diǎn):消耗大量的操作系統(tǒng)資源。多個(gè)線程共享一個(gè)處理器將導(dǎo)致操作系統(tǒng)忙于管理這些線程,而無法運(yùn)行程序。
二、創(chuàng)建線程
class Program { static void Main(string[] args) { Thread t1 = new Thread(new ThreadStart(PrintNumbers));//無參數(shù)的委托,把方法的引用當(dāng)做參數(shù) t1.Start(); Thread t2 = new Thread(new ParameterizedThreadStart(PrintNumbers));//有參數(shù)的委托,把方法的引用當(dāng)做參數(shù) t2.Start(10); Console.ReadLine(); } static void PrintNumbers() { Console.WriteLine("1.Starting..."); for (int i = 0; i < 10; i++) { Console.WriteLine(i); } } //注意:要使用ParameterizedThreadStart,定義的參數(shù)必須為object static void PrintNumbers(object count) { Console.WriteLine("2.Starting..."); for (int i = 0; i < Convert.ToInt32(count); i++) { Console.WriteLine(i); } } } }
注釋:
1.我們只需指定在不同線程運(yùn)行的方法名,而C#編譯器會(huì)在后臺(tái)創(chuàng)建這些對(duì)象。
2.要使用ParameterizedThreadStart,定義的參數(shù)必須為object類型。
三、暫停線程
class Program { static void Main(string[] args) { Thread t1 = new Thread(PrintNumbersWithDelay); t1.Start(); PrintNumbers(); Console.ReadLine(); } static void PrintNumbers() { Console.WriteLine("1.Starting..."); for (int i = 0; i < 10; i++) { Console.WriteLine("In 1.Starting: " + i); } } static void PrintNumbersWithDelay() { Console.WriteLine("2.Starting..."); for (int i = 0; i < 10; i++) { //var a = TimeSpan.FromSeconds(2); Thread.Sleep(TimeSpan.FromSeconds(2));//暫停兩秒 Console.WriteLine("In 2.Starting: " + i); } } }
注釋:使用Thread.Sleep(TimeSpan.FromSeconds(2));暫停線程一段時(shí)間
四、線程等待
class Program { static void Main(string[] args) { Console.WriteLine("Starting..."); Thread t = new Thread(PrintNumbersWithDelay); t.Start(); t.Join(); //使用Join等待t完成后,再向下執(zhí)行PrintNumbers,如果注釋掉輸出明顯不同 PrintNumbers(); Console.WriteLine("Thread Complete"); Console.ReadLine(); } static void PrintNumbers() { Console.WriteLine("1.Starting..."); for (int i = 0; i < 10; i++) { Console.WriteLine("In 1.Starting:" + i); } } static void PrintNumbersWithDelay() { Console.WriteLine("2.Starting..."); for (int i = 0; i < 10; i++) { Thread.Sleep(TimeSpan.FromSeconds(2)); Console.WriteLine("In 2.Starting:" + i); } } }
注釋:使用t.Join(); 等待t完成。
五、終止線程
class Program { static void Main(string[] args) { Console.WriteLine("Starting Program..."); Thread t1 = new Thread(PrintNumbersWithDelay); t1.Start(); Thread.Sleep(TimeSpan.FromSeconds(7));//此時(shí)t1線程會(huì)執(zhí)行7秒 t1.Abort(); //使用Abort()終止線程 Console.WriteLine("Thread t1 has been aborted"); Thread t2 = new Thread(PrintNumbers); t2.Start(); Console.ReadLine(); } static void PrintNumbers() { Console.WriteLine("1.Starting..."); for (int i = 0; i < 10; i++) { Console.WriteLine("In 1.Starting:" + i); } } static void PrintNumbersWithDelay() { Console.WriteLine("2.Starting..."); for (int i = 0; i < 10; i++) { Thread.Sleep(TimeSpan.FromSeconds(2)); Console.WriteLine("In 2.Starting:" + i); } } }
注釋:使用Thread實(shí)例的Abort方法終止線程。
六、檢測線程狀態(tài)
class Program { static void Main(string[] args) { Console.WriteLine("Start Program..."); Thread t1 = new Thread(PrintNumbersWithStatus); Thread t2 = new Thread(DoNothing); Console.WriteLine("t1 status:" + t1.ThreadState.ToString());//獲取實(shí)例線程狀態(tài) t2.Start(); t1.Start(); for (int i = 0; i < 30; i++) { Console.WriteLine("t1 status:" + t1.ThreadState.ToString() + "\t" + "t2 status:" + t2.ThreadState.ToString()); } Thread.Sleep(TimeSpan.FromSeconds(7)); t1.Abort(); Console.WriteLine("thread t1 has been aborted"); Console.WriteLine("t1 status:" + t1.ThreadState.ToString()); Console.WriteLine("t2 status:" + t2.ThreadState.ToString()); Console.ReadLine(); } private static void PrintNumbersWithStatus() { Console.WriteLine("1.Starting..."); Console.WriteLine("In 1.Starting t1 status:" + Thread.CurrentThread.ThreadState.ToString());//獲取當(dāng)前線程狀態(tài) for (int i = 0; i < 10; i++) { Thread.Sleep(TimeSpan.FromSeconds(2)); Console.WriteLine("In 1.Starting:" + i); } } private static void DoNothing() { Thread.Sleep(TimeSpan.FromSeconds(2)); Console.WriteLine("t2 Console..."); } }
注釋:使用Thread.ThreadState獲取線程的運(yùn)行狀態(tài)。ThreadState是一個(gè)C#枚舉。謹(jǐn)記:不要在程序中使用線程終止,否則可能會(huì)出現(xiàn)意想不到的結(jié)果
七、線程優(yōu)先級(jí)
class Program { static void Main(string[] args) { //讓操作系統(tǒng)的所有線程運(yùn)行在多個(gè)CPU核心上 Console.WriteLine($"Current thread priority: {Thread.CurrentThread.Priority}"); Console.WriteLine("Running on all cores available");//獲取實(shí)例線程狀態(tài) RunThreads(); Thread.Sleep(TimeSpan.FromSeconds(2)); Console.WriteLine("Running on a single Core"); //讓操作系統(tǒng)的所有線程運(yùn)行在單個(gè)CPU核心上 Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); RunThreads(); Console.ReadLine(); } private static void RunThreads() { var sample = new ThreadSample(); var t1 = new Thread(sample.CountNumbers); t1.Name = "Thread One"; var t2 = new Thread(sample.CountNumbers); t2.Name = "Thread Two"; t1.Priority = ThreadPriority.Highest;//使用Priority設(shè)置線程的優(yōu)先級(jí) t2.Priority = ThreadPriority.Lowest; t1.Start(); t2.Start();//此處t2優(yōu)先級(jí)低于t1,t2等待t1釋放資源。 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($"{Thread.CurrentThread.Name} with {Thread.CurrentThread.Priority} priority has a count={counter.ToString("N0")}"); } }
注釋:單核執(zhí)行多線程耗費(fèi)的時(shí)間比多核的多很多。
八、前臺(tái)線程和后臺(tái)線程
class Program { static void Main(string[] args) { var sampleForground = new ThreadSample(10); var sampleBackground = new ThreadSample(20); var t1 = new Thread(sampleForground.CountNumbers);//方法的引用 t1.Name = "ForegroundThread"; //沒有明確聲明的均為前臺(tái)線程 var t2 = new Thread(sampleBackground.CountNumbers); t2.Name = "BackgroundThread"; t2.IsBackground = true; //設(shè)置為后臺(tái)線程 t1.Start(); t2.Start(); Console.ReadLine(); } } class ThreadSample { private readonly int _iteration; public ThreadSample(int iteration) { _iteration = iteration; } public void CountNumbers() { for (int i = 0; i < _iteration; i++) { Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}"); } } }
注釋:進(jìn)程會(huì)等待所有的前臺(tái)線程完成后再結(jié)束工作,但是如果只剩下后臺(tái)線程,則會(huì)直接結(jié)束工作。
九、向線程傳遞參數(shù)
class Program { static void Main(string[] args) { ThreadSample sample = new ThreadSample(5); Thread t1 = new Thread(sample.CountNumbers); t1.Name = "ThreadOne"; t1.Start(); t1.Join(); Console.WriteLine("--------------------------"); Thread t2 = new Thread(Count); t2.Name = "ThreadTwo"; t2.Start(3); t2.Join(); Console.WriteLine("--------------------------"); //使用lambda表達(dá)式引用另一個(gè)C#對(duì)方的方式被稱為閉包。當(dāng)在lambda表達(dá)式中使用任何局部變量時(shí),C#會(huì)生成一個(gè)類,并將該變量作為該類的一個(gè)屬性,但是我們無須定義該類,C#編譯器會(huì)自動(dòng)幫我們實(shí)現(xiàn) Thread t3 = new Thread(() => CountNumbers(5)); t3.Name = "ThreadThree"; t3.Start(); t3.Join(); Console.WriteLine("--------------------------"); int i = 10; Thread t4 = new Thread(() => PrintNumber(i)); i = 20; Thread t5 = new Thread(() => PrintNumber(i)); t4.Start(); t5.Start(); //t4, t5都會(huì)輸出20, 因?yàn)閠4,t5沒有Start之前i已經(jīng)變成20了 Console.ReadKey(); } 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($"{Thread.CurrentThread.Name} prints {i}"); } } static void PrintNumber(int number) { Console.WriteLine(number); } } class ThreadSample { private readonly int _iteration; public ThreadSample(int iteration) { _iteration = iteration; } public void CountNumbers() { for (int i = 1; i <= _iteration; i++) { Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}"); } } }
十、使用C# Lock 關(guān)鍵字
class Program { static void Main(string[] args) { Console.WriteLine("Incorrect Counter"); Counter c1 = new Counter(); var t1 = new Thread(() => TestCounter(c1)); var t2 = new Thread(() => TestCounter(c1)); var t3 = new Thread(() => TestCounter(c1)); t1.Start(); t2.Start(); t3.Start(); t1.Join(); t2.Join(); t3.Join(); Console.WriteLine($"Total Count: {c1.Count}"); Console.WriteLine("------------------------"); //使用LOCK關(guān)鍵字,Count同一時(shí)刻只允許一個(gè)線程訪問 Console.WriteLine("Correct counter"); CounterWithLock c2 = new CounterWithLock(); t1 = new Thread(() => TestCounter(c2)); t2 = new Thread(() => TestCounter(c2)); t3 = new Thread(() => TestCounter(c2)); t1.Start(); t2.Start(); t3.Start(); t1.Join(); t2.Join(); t3.Join(); Console.WriteLine($"Total count:{c2.Count}"); Console.ReadLine(); } static void TestCounter(CounterBase c) { for (int i = 0; i < 100000; i++) { c.Increment(); c.Decrement(); } } //子類 class Counter : CounterBase { public int Count { get; private set; } //重寫基類方法 public override void Decrement() { Count--; } public override void Increment() { Count++; } } //子類 class CounterWithLock : CounterBase { private readonly object _asyncRoot = new object(); public int Count { get; private set; } //重寫基類方法 public override void Decrement() { lock (_asyncRoot) { Count--; } } public override void Increment() { lock (_asyncRoot) { Count++; } } } //基類 abstract class CounterBase { public abstract void Increment(); public abstract void Decrement(); } } class ThreadSample { private readonly int _iteration; public ThreadSample(int iteration)//構(gòu)造函數(shù) { _iteration = iteration; } public void CountNumbers() { for (int i = 1; i <= _iteration; i++) { Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine($"{Thread.CurrentThread.Name} prints {i}"); } } }
注釋:不加鎖,得出的結(jié)果不確定,競爭條件下很容易出錯(cuò)。加鎖得出的結(jié)果是正確的,但是性能受到了影響
十一、使用Monitor類鎖定資源
class Program { static void Main(string[] args) { object lock1 = new object(); object lock2 = new object(); new Thread(() => LockTooMuch(lock1, lock2)).Start(); lock (lock2) { Thread.Sleep(1000); Console.WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed"); //直接使用Monitor.TryEnter, 如果在第二個(gè)參數(shù)之前還未獲取到lock保護(hù)的資源會(huì)返回false if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5))) { Console.WriteLine("Acquired a protected resource successfully"); } else { Console.WriteLine("Timeout acquiring a resource"); } } new Thread(() => LockTooMuch(lock1, lock2)).Start(); Console.WriteLine("-----------------------------"); /* 下面代碼會(huì)造成死鎖, 所以注釋掉 lock (lock2) { Console.WriteLine("This will be a deadlock!"); Thread.Sleep(1000); lock (lock1) { Console.WriteLine("Acquired a protected resource successfully"); } } */ } static void LockTooMuch(object lock1, object lock2) { lock (lock1) { Thread.Sleep(1000); lock (lock2); } } }
注釋:Monitor.TryEnter在指定的時(shí)間內(nèi)嘗試獲取指定對(duì)象上的排他鎖
十二、處理異常
class Program { static void Main(string[] args) { Thread 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 bad faulty thread....."); Thread.Sleep(TimeSpan.FromSeconds(2)); //這個(gè)異常主線程無法捕捉到,因?yàn)槭窃谧泳€程拋出的異常。需要在子線程中加入try...catch捕獲異常 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: {ex.Message}"); } } }
到此這篇關(guān)于c#多線程之線程基礎(chǔ)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Unity 百度AI實(shí)現(xiàn)人像動(dòng)漫化效果
這篇文章主要介紹了Unity如何接入百度AI接口, 運(yùn)用對(duì)抗生成網(wǎng)絡(luò)技術(shù),為用戶量身定制千人千面的二次元?jiǎng)勇蜗?,并支持通過參數(shù)設(shè)置,生成二次元?jiǎng)勇讼?。感興趣的可以學(xué)習(xí)一下2022-01-01基于c# 類、接口、結(jié)構(gòu)的聯(lián)系與區(qū)別詳解
本篇文章是對(duì)c#中類與接口以及結(jié)構(gòu)的聯(lián)系與區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06C#實(shí)現(xiàn)將DataTable內(nèi)容輸出到Excel表格的方法
這篇文章主要介紹了C#實(shí)現(xiàn)將DataTable內(nèi)容輸出到Excel表格的方法,較為詳細(xì)的分析了C#基于DataTable保存Excel數(shù)據(jù)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08