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

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

