C#中Thread(線程)和Task(任務(wù))實(shí)例詳解
線程
線程:對(duì)于所有需要等待的操作,例如移動(dòng)文件,數(shù)據(jù)庫(kù)和網(wǎng)絡(luò)訪問(wèn)都需要一定的時(shí)間,此時(shí)就可以啟動(dòng)一個(gè)新的線程,同時(shí)完成其他任務(wù)。一個(gè)進(jìn)程的多個(gè)線程可以同時(shí)運(yùn)行在不同的CPU上或多核CPU的不同內(nèi)核上。
一個(gè)應(yīng)用程序啟動(dòng)時(shí),會(huì)啟動(dòng)一個(gè)進(jìn)程(應(yīng)用程序的載體),然后進(jìn)程會(huì)啟動(dòng)多個(gè)線程。
一,使用Thread類(lèi)啟動(dòng)線程和數(shù)據(jù)傳輸
使用Thread類(lèi)可以創(chuàng)建和控制線程,Thread構(gòu)造函數(shù)是一個(gè)無(wú)參無(wú)返回值的委托類(lèi)型。
1??對(duì)Thread傳入一個(gè)無(wú)參無(wú)返回類(lèi)型的方法-ThreadStart。
public delegate void ThreadStart();
實(shí)例:
static void test()
{
Console.WriteLine("test is start");
Console.WriteLine("test is running");
Thread.Sleep(3000);
Console.WriteLine("test is completed");
}
static void Main(string[] args)
{
Thread th = new Thread(test);
th.Start();
Console.WriteLine("main is completed");
}
2??對(duì)Thread傳入一個(gè)匿名方法(或lambda表達(dá)式)。用于傳入的方法代碼簡(jiǎn)單的情況
static void Main(string[] args)
{
//lambad表達(dá)式
Thread th = new Thread(()=> {
Console.WriteLine("子線程1-ID:" + Thread.CurrentThread.ManagedThreadId);
});
th.Start();
//匿名方法
Thread th2 = new Thread(delegate ()
{
Console.WriteLine("子線程2-ID:" + Thread.CurrentThread.ManagedThreadId);
});
th2.Start();
Console.WriteLine("main is completed");
}
3??對(duì)Thread傳入一個(gè)無(wú)返回值有參方法-ParameterizedThreadStart,該參數(shù)只能是object類(lèi)型且只能有一個(gè)參數(shù)。
public delegate void ParameterizedThreadStart(object? obj);
實(shí)例:
static void download(object o)
{
string str = o as string;
Console.WriteLine("地址:" + str);
}
static void Main(string[] args)
{
Thread th = new Thread(download);
th.Start("http://baidu.com");
Console.WriteLine("main is completed");
}
注意:使用as進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換 成功會(huì)正確輸出,失敗會(huì)輸出null。
以上數(shù)據(jù)傳輸?shù)姆椒ǘ际腔陟o態(tài)變量進(jìn)行傳輸?shù)模嵌x過(guò)多靜態(tài)變量會(huì)導(dǎo)致多個(gè)線程訪問(wèn)同一個(gè)靜態(tài)變量,造成資源沖突。
靜態(tài)變量雖然方便訪問(wèn),但是靜態(tài)的一般都是公共的,容易混亂。
4??對(duì)Thread傳入一個(gè)無(wú)返回值多個(gè)參數(shù)的方法(定義一個(gè)結(jié)構(gòu)體),但是不能用as強(qiáng)制轉(zhuǎn)換。
public struct data
{
public string message;
public int age;
}
static void download(object o)
{
data str = (data)o;//強(qiáng)制類(lèi)型轉(zhuǎn)換
Console.WriteLine("信息:" + str.message);
Console.WriteLine("年紀(jì):" + str.age);
}
static void Main(string[] args)
{
data da = new data();
da.message = "sss";
da.age = 3;
Thread th = new Thread(download);
th.Start(da);
Console.WriteLine("main is completed");
}由于結(jié)構(gòu)體是值類(lèi)型,不能為null,因此不能用as進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換。
5??通過(guò)自定義類(lèi)傳遞數(shù)據(jù)(即將通過(guò)線程調(diào)用一個(gè)類(lèi)的成員方法)
先創(chuàng)建一個(gè)download類(lèi):
class downLoad
{
public string URL { get; private set; }
public string Message { get; private set; }
//構(gòu)造函數(shù)
public downLoad(string uRL, string message)
{
URL = uRL;
Message = message;
}
//下載方法
public void load()
{
Console.WriteLine("從" + URL + "獲取信息:" + Message);
}
}在主程序中將該類(lèi)的成員方法傳入Thread中:
static void Main(string[] args)
{
var download = new downLoad("www.baidu.com", "mp4");
Thread th = new Thread(download.load);
th.Start();
Console.WriteLine("main is completed");
Console.ReadKey();
}??知識(shí)點(diǎn)拓展1-前臺(tái)線程與后臺(tái)線程:
應(yīng)用程序的進(jìn)程需要等待所有前臺(tái)線程完成其任務(wù)后才會(huì)結(jié)束。而后臺(tái)線程在應(yīng)用程序關(guān)閉后會(huì)自動(dòng)關(guān)閉,即使后臺(tái)線程還沒(méi)有執(zhí)行完畢。在默認(rèn)情況下,用Thread類(lèi)創(chuàng)建的線程是前臺(tái)線程,線程池中的線程是后臺(tái)線程。在Thread類(lèi)創(chuàng)建線程的時(shí)候,可以設(shè)置IsBackground屬性,表示它是否是一個(gè)后臺(tái)線程。
??知識(shí)點(diǎn)拓展2-線程的優(yōu)先級(jí)
線程有操作系統(tǒng)調(diào)度,一個(gè)CPU同一時(shí)間只能做一件事(運(yùn)行一個(gè)線程中的計(jì)算任務(wù)),當(dāng)有很多線程需要CPU執(zhí)行時(shí),線程調(diào)度器會(huì)根據(jù)線程的優(yōu)先級(jí)去判斷先去執(zhí)行哪個(gè)線程,如果優(yōu)先級(jí)相同,就使用一個(gè)循環(huán)調(diào)度規(guī)則,逐個(gè)執(zhí)行每個(gè)線程。
在Thread類(lèi)中,可以設(shè)置Priority屬性,以影響線程的基本優(yōu)先級(jí),Priority屬性一個(gè)ThreadPriority枚舉定義的一個(gè)值,定義級(jí)別有Highest,AboveNormal,Normal,BelowNormal,和Lowest。
因此對(duì)于重要的線程任務(wù),可以將線程優(yōu)先級(jí)設(shè)置高一點(diǎn),使其可以盡快執(zhí)行完畢。
如果需要等待線程執(zhí)行結(jié)果在執(zhí)行后面的代碼,可以調(diào)用Thread對(duì)象的join方法,即將該線程加入進(jìn)來(lái),并停止當(dāng)前線程,直至加入的線程執(zhí)行完畢。
二,線程池ThreadPool類(lèi)
由于線程的創(chuàng)建需要時(shí)間,如果有不同的小任務(wù)要完成,就可以事先創(chuàng)建多個(gè)線程。系統(tǒng)有一個(gè)ThreadPool類(lèi)來(lái)管理線程,這個(gè)類(lèi)會(huì)在需要線程的時(shí)候增加線程數(shù),不需要時(shí)候減少。池中最大線程數(shù)是可配置的。在雙核CPU中,默認(rèn)設(shè)置為1023個(gè)工作線程和1000個(gè)IO線程,如果需要更多線程(超過(guò)了線程池的最大數(shù)量),最新的任務(wù)就需要排隊(duì)等待。
使用線程池,即調(diào)用ThreadPool.QueueUserWorkItem方法,該方法需要傳入一個(gè)WaitCallBack類(lèi)型的委托(即傳入帶一個(gè)object參數(shù)的方法)。然后ThreadPool會(huì)在池中找一個(gè)空閑的線程去執(zhí)行傳入的方法。
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(work);
}
Thread.Sleep(10000);
}
static void work(object state)
{
Console.WriteLine("線程id" + Thread.CurrentThread.ManagedThreadId);
}
需要注意的是:
線程池中所有線程都是后臺(tái)線程,如果進(jìn)程中所有的前臺(tái)線程都結(jié)束了,所有的后臺(tái)線程也會(huì)跟著結(jié)束。不能把入池的后臺(tái)線程改為前臺(tái)線程。不能給入池的線程設(shè)置優(yōu)先級(jí)或名稱(chēng)。入池的線程只能是用于時(shí)間較短的任務(wù)。如果線程要一直運(yùn)行,就應(yīng)用Thread類(lèi)創(chuàng)建一個(gè)線程。
任務(wù)
任務(wù)表示應(yīng)完成某個(gè)單元的工作,這個(gè)工作可以在單獨(dú)的線程中運(yùn)行,也可以同步方式啟動(dòng)一個(gè)任務(wù)。任務(wù)在后臺(tái)使用ThreadPool進(jìn)行管理的,也就是說(shuō)任務(wù)啟動(dòng)的也是后臺(tái)線程。
一,創(chuàng)建并啟動(dòng)任務(wù)
啟動(dòng)任務(wù)的兩種方式:
static void Main(string[] args)
{
//第一種:使用TaskFactory
TaskFactory tf = new TaskFactory();
tf.StartNew(work);
//第二種:使用Task
Task t = new Task(work);
t.Start();
Console.WriteLine("main is completed");
}
static void work()
{
Console.WriteLine("線程id" + Thread.CurrentThread.ManagedThreadId);
}
需要注意的是:使用TaskFactory創(chuàng)建任務(wù),傳入的方法為無(wú)參的。
二,連續(xù)任務(wù)
如果一個(gè)任務(wù)t1的執(zhí)行是依賴(lài)于另一個(gè)任務(wù)t2,那么就需要在t2執(zhí)行完畢后才開(kāi)始執(zhí)行t1。(例如:迅雷下載完成后彈出界面提示)這時(shí)候我們可以使用連續(xù)任務(wù)ContinueWith。
static void Main(string[] args)
{
Task t1 = new Task(download);
Task t2 = t1.ContinueWith(show);
t1.Start();
Thread.Sleep(2000);
}
static void download()
{
Console.WriteLine("正在下載中。。。。");
Thread.Sleep(1000);
}
static void show(Task t)
{
Console.WriteLine("下載完成!");
}
需要注意的是:傳入t2的方法的參數(shù)需為T(mén)ask類(lèi)型。
三,資源沖突問(wèn)題
在多線程中如果多個(gè)線程同時(shí)訪問(wèn)同一資源,就會(huì)產(chǎn)生資源沖突的問(wèn)題。這時(shí)候需要用lock對(duì)程序進(jìn)行加鎖。
??什么是資源沖突?
class state
{
public int num = 5;
public void checknum()
{
if (num==5)
{
num++;
Console.WriteLine("num的值:" + num + "當(dāng)前線程id:" + Thread.CurrentThread.ManagedThreadId);
}
num =5;
}
} static void Main(string[] args)
{
state st = new state();
for (int i = 0; i < 10; i++)
{
Thread th = new Thread(st.checknum);
th.Start();
}
}在主程序使用多線程調(diào)用state類(lèi)的check方法,可以看到num=5和num=6造成資源沖突了。

??對(duì)多個(gè)線程訪問(wèn)的對(duì)象進(jìn)行加鎖
object _lock = new object();
public int num = 5;
public void checknum()
{
lock(_lock)
{
if (num==5)
{
num++;
Console.WriteLine("num的值:" + num + "當(dāng)前線程id:" + Thread.CurrentThread.ManagedThreadId);
}
num =5;
}
}
總結(jié)
到此這篇關(guān)于C#中Thread(線程)和Task(任務(wù))的文章就介紹到這了,更多相關(guān)C# 線程和任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C# 二進(jìn)制數(shù)組與結(jié)構(gòu)體的互轉(zhuǎn)方法
本文將和大家介紹 MemoryMarshal 輔助類(lèi),通過(guò)這個(gè)輔助類(lèi)用來(lái)實(shí)現(xiàn)結(jié)構(gòu)體數(shù)組和二進(jìn)制數(shù)組的相互轉(zhuǎn)換,對(duì)C# 二進(jìn)制數(shù)組與結(jié)構(gòu)體的互轉(zhuǎn)方法感興趣的朋友一起看看吧2023-09-09
WPF數(shù)據(jù)綁定時(shí)出現(xiàn)StringFormat失效的原因和解決方法
在數(shù)據(jù)綁定過(guò)程中,我們經(jīng)常會(huì)使用StringFormat對(duì)要顯示的數(shù)據(jù)進(jìn)行格式化,以便獲得更為直觀的展示效果,但在某些情況下格式化操作并未生效,所以本文介紹了WPF數(shù)據(jù)綁定時(shí)出現(xiàn)StringFormat失效的原因和解決方法,需要的朋友可以參考下2024-12-12
C#中Byte轉(zhuǎn)換相關(guān)的函數(shù)
這篇文章主要介紹了C#中Byte轉(zhuǎn)換相關(guān)的函數(shù)介紹,非常具有參考借鑒價(jià)值,特此分享到腳本之家平臺(tái)供大家學(xué)習(xí)2016-05-05
C#自定義類(lèi)型強(qiáng)制轉(zhuǎn)換實(shí)例分析
這篇文章主要介紹了C#自定義類(lèi)型強(qiáng)制轉(zhuǎn)換的方法,實(shí)例分析了C#類(lèi)型轉(zhuǎn)換的相關(guān)技巧,需要的朋友可以參考下2015-05-05
C#序列化與反序列化(Serialize,Deserialize)實(shí)例詳解
這篇文章主要介紹了C#序列化與反序列化(Serialize,Deserialize)的方法,實(shí)例分析了C#序列化與反序列化的常見(jiàn)技巧,需要的朋友可以參考下2015-06-06
精簡(jiǎn)高效的C#網(wǎng)站優(yōu)化經(jīng)驗(yàn)技巧總結(jié)
這篇文章主要為大家介紹了精簡(jiǎn)高效的C#網(wǎng)站優(yōu)化經(jīng)驗(yàn)技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
DevExpress實(shí)現(xiàn)TreeList按條件隱藏節(jié)點(diǎn)CheckBox的方法
這篇文章主要介紹了DevExpress實(shí)現(xiàn)TreeList按條件隱藏節(jié)點(diǎn)CheckBox的方法,需要的朋友可以參考下2014-08-08
C# Winform調(diào)用百度接口實(shí)現(xiàn)人臉識(shí)別教程(附源碼)
這篇文章主要介紹了C# Winform調(diào)用百度接口實(shí)現(xiàn)人臉識(shí)別教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05

