c#多線程之線程基礎(chǔ)
一、簡介
1.為了防止一個應(yīng)用程序控制CPU而導(dǎo)致其他應(yīng)用程序和操作系統(tǒng)本身永遠(yuǎn)被掛起這一可能情況,操作系統(tǒng)不得不使用某種方式將物理計算分割為一些虛擬的進(jìn)程,并給予每個執(zhí)行程序一定量的計算能力。此外操作系統(tǒng)必須始終能夠優(yōu)先訪問CPU,并能調(diào)整不同程序訪問CPU的優(yōu)先級。線程正式這一慨念的實現(xiàn)。
2.多線程優(yōu)缺點:
多線程優(yōu)點:可以同時執(zhí)行多個計算任務(wù),有可能提高計算機的處理能力,使得計算機每秒能執(zhí)行越來越多的命令
多線程缺點:消耗大量的操作系統(tǒng)資源。多個線程共享一個處理器將導(dǎo)致操作系統(tǒng)忙于管理這些線程,而無法運行程序。
二、創(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.我們只需指定在不同線程運行的方法名,而C#編譯器會在后臺創(chuàng)建這些對象。
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));暫停線程一段時間
四、線程等待
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));//此時t1線程會執(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實例的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());//獲取實例線程狀態(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獲取線程的運行狀態(tài)。ThreadState是一個C#枚舉。謹(jǐn)記:不要在程序中使用線程終止,否則可能會出現(xiàn)意想不到的結(jié)果
七、線程優(yōu)先級
class Program
{
static void Main(string[] args)
{
//讓操作系統(tǒng)的所有線程運行在多個CPU核心上
Console.WriteLine($"Current thread priority: {Thread.CurrentThread.Priority}");
Console.WriteLine("Running on all cores available");//獲取實例線程狀態(tài)
RunThreads();
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("Running on a single Core");
//讓操作系統(tǒng)的所有線程運行在單個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)先級
t2.Priority = ThreadPriority.Lowest;
t1.Start();
t2.Start();//此處t2優(yōu)先級低于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í)行多線程耗費的時間比多核的多很多。
八、前臺線程和后臺線程
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"; //沒有明確聲明的均為前臺線程
var t2 = new Thread(sampleBackground.CountNumbers);
t2.Name = "BackgroundThread";
t2.IsBackground = true; //設(shè)置為后臺線程
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)程會等待所有的前臺線程完成后再結(jié)束工作,但是如果只剩下后臺線程,則會直接結(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á)式引用另一個C#對方的方式被稱為閉包。當(dāng)在lambda表達(dá)式中使用任何局部變量時,C#會生成一個類,并將該變量作為該類的一個屬性,但是我們無須定義該類,C#編譯器會自動幫我們實現(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都會輸出20, 因為t4,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同一時刻只允許一個線程訪問
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é)果不確定,競爭條件下很容易出錯。加鎖得出的結(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, 如果在第二個參數(shù)之前還未獲取到lock保護(hù)的資源會返回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("-----------------------------");
/* 下面代碼會造成死鎖, 所以注釋掉
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在指定的時間內(nèi)嘗試獲取指定對象上的排他鎖
十二、處理異常
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));
//這個異常主線程無法捕捉到,因為是在子線程拋出的異常。需要在子線程中加入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ǔ)的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C#基礎(chǔ):基于const與readonly的深入研究
本篇文章是對c#中const與readonly進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
深入多線程之:Reader與Write Locks(讀寫鎖)的使用詳解
本篇文章是對Reader與Write Locks(讀寫鎖)的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
利用C#/VB.NET實現(xiàn)PPT轉(zhuǎn)換為HTML
利用PowerPoint可以很方便的呈現(xiàn)多媒體信息,且信息形式多媒體化,表現(xiàn)力強。但難免在某些情況下我們會需要將PowerPoint轉(zhuǎn)換為HTML格式,本文就為大家整理了轉(zhuǎn)換方法,希望對大家有所幫助2023-05-05
C# 串口接收數(shù)據(jù)中serialPort.close()死鎖的實例
下面小編就為大家分享一篇C# 串口接收數(shù)據(jù)中serialPort.close()死鎖的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-11-11

