狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

C#中 Thread,Task,Async/Await,IAsyncResult 的那些事兒!

admin
2024年1月3日 9:9 本文熱度 975

說(shuō)起異步,Thread,Task,async/await,IAsyncResult 這些東西肯定是繞不開(kāi)的,今天就來(lái)依次聊聊他們

1.線(xiàn)程(Thread)

多線(xiàn)程的意義在于一個(gè)應(yīng)用程序中,有多個(gè)執(zhí)行部分可以同時(shí)執(zhí)行;對(duì)于比較耗時(shí)的操作(例如io,數(shù)據(jù)庫(kù)操作),或者等待響應(yīng)(如WCF通信)的操作,可以單獨(dú)開(kāi)啟后臺(tái)線(xiàn)程來(lái)執(zhí)行,這樣主線(xiàn)程就不會(huì)阻塞,可以繼續(xù)往下執(zhí)行;等到后臺(tái)線(xiàn)程執(zhí)行完畢,再通知主線(xiàn)程,然后做出對(duì)應(yīng)操作!

在C#中開(kāi)啟新線(xiàn)程比較簡(jiǎn)單

static void Main(string[] args)

{

    Console.WriteLine("主線(xiàn)程開(kāi)始");

    //IsBackground=true,將其設(shè)置為后臺(tái)線(xiàn)程

    Thread t = new Thread(Run) { IsBackground = true };

    t.Start();
   Console.WriteLine("主線(xiàn)程在做其他的事!");

    //主線(xiàn)程結(jié)束,后臺(tái)線(xiàn)程會(huì)自動(dòng)結(jié)束,不管有沒(méi)有執(zhí)行完成

    //Thread.Sleep(300);

    Thread.Sleep(1500);

    Console.WriteLine("主線(xiàn)程結(jié)束");

}

 

static void Run()

{

    Thread.Sleep(700);

Console.WriteLine("這是后臺(tái)線(xiàn)程調(diào)用");

}

 執(zhí)行結(jié)果如下圖,

可以看到在啟動(dòng)后臺(tái)線(xiàn)程之后,主線(xiàn)程繼續(xù)往下執(zhí)行了,并沒(méi)有等到后臺(tái)線(xiàn)程執(zhí)行完之后。

1.1 線(xiàn)程池

試想一下,如果有大量的任務(wù)需要處理,例如網(wǎng)站后臺(tái)對(duì)于HTTP請(qǐng)求的處理,那是不是要對(duì)每一個(gè)請(qǐng)求創(chuàng)建一個(gè)后臺(tái)線(xiàn)程呢?顯然不合適,這會(huì)占用大量?jī)?nèi)存,而且頻繁地創(chuàng)建的過(guò)程也會(huì)嚴(yán)重影響速度,那怎么辦呢?線(xiàn)程池就是為了解決這一問(wèn)題,把創(chuàng)建的線(xiàn)程存起來(lái),形成一個(gè)線(xiàn)程池(里面有多個(gè)線(xiàn)程),當(dāng)要處理任務(wù)時(shí),若線(xiàn)程池中有空閑線(xiàn)程(前一個(gè)任務(wù)執(zhí)行完成后,線(xiàn)程不會(huì)被回收,會(huì)被設(shè)置為空閑狀態(tài)),則直接調(diào)用線(xiàn)程池中的線(xiàn)程執(zhí)行(例asp.net處理機(jī)制中的Application對(duì)象),

使用事例:

for (int i = 0; i < 10; i++)

{

    ThreadPool.QueueUserWorkItem(m =>

    {

        Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString());

    });

}

Console.Read();

運(yùn)行結(jié)果:

可以看到,雖然執(zhí)行了10次,但并沒(méi)有創(chuàng)建10個(gè)線(xiàn)程。

 1.2 信號(hào)量(Semaphore)

 Semaphore負(fù)責(zé)協(xié)調(diào)線(xiàn)程,可以限制對(duì)某一資源訪問(wèn)的線(xiàn)程數(shù)量

 這里對(duì)SemaphoreSlim類(lèi)的用法做一個(gè)簡(jiǎn)單的事例:

static SemaphoreSlim semLim = new SemaphoreSlim(3); //3表示最多只能有三個(gè)線(xiàn)程同時(shí)訪問(wèn)

static void Main(string[] args)

{

    for (int i = 0; i < 10; i++)

    {

        new Thread(SemaphoreTest).Start();

    }

    Console.Read();

}

 

static void SemaphoreTest()

{

    semLim.Wait();

    Console.WriteLine("線(xiàn)程" + Thread.CurrentThread.ManagedThreadId.ToString() + "開(kāi)始執(zhí)行");

Thread.Sleep(2000);

    Console.WriteLine("線(xiàn)程" + Thread.CurrentThread.ManagedThreadId.ToString() + "執(zhí)行完畢");

    semLim.Release();

}

執(zhí)行結(jié)果如下:

可以看到,剛開(kāi)始只有三個(gè)線(xiàn)程在執(zhí)行,當(dāng)一個(gè)線(xiàn)程執(zhí)行完畢并釋放之后,才會(huì)有新的線(xiàn)程來(lái)執(zhí)行方法!

除了SemaphoreSlim類(lèi),還可以使用Semaphore類(lèi),感覺(jué)更加靈活,感興趣的話(huà)可以搜一下,這里就不做演示了!

2.Task

Task是.NET4.0加入的,跟線(xiàn)程池ThreadPool的功能類(lèi)似,用Task開(kāi)啟新任務(wù)時(shí),會(huì)從線(xiàn)程池中調(diào)用線(xiàn)程,而Thread每次實(shí)例化都會(huì)創(chuàng)建一個(gè)新的線(xiàn)程。

Console.WriteLine("主線(xiàn)程啟動(dòng)");

//Task.Run啟動(dòng)一個(gè)線(xiàn)程

//Task啟動(dòng)的是后臺(tái)線(xiàn)程,要在主線(xiàn)程中等待后臺(tái)線(xiàn)程執(zhí)行完畢,可以調(diào)用Wait方法

Task task = Task.Factory.StartNew(() => {

    Thread.Sleep(1500);

    Console.WriteLine("task啟動(dòng)");

});

Task task = Task.Run(() => {

    Thread.Sleep(1500);

    Console.WriteLine("task啟動(dòng)");

});

Thread.Sleep(300);

task.Wait();

Console.WriteLine("主線(xiàn)程結(jié)束");

執(zhí)行結(jié)果如下:

開(kāi)啟新任務(wù)的方法:Task.Run()或者Task.Factory.StartNew(),開(kāi)啟的是后臺(tái)線(xiàn)程

要在主線(xiàn)程中等待后臺(tái)線(xiàn)程執(zhí)行完畢,可以使用Wait方法(會(huì)以同步的方式來(lái)執(zhí)行)。不用Wait則會(huì)以異步的方式來(lái)執(zhí)行。

比較一下Task和Thread:

static void Main(string[] args)

{

    for (int i = 0; i < 5; i++)

    {

        new Thread(Run1).Start();

    }

    for (int i = 0; i < 5; i++)

    {

        Task.Run(() => { Run2(); });

    }

}

static void Run1()

{

    Console.WriteLine("Thread Id =" + Thread.CurrentThread.ManagedThreadId);

}

static void Run2()

{

    Console.WriteLine("Task調(diào)用的Thread Id =" + Thread.CurrentThread.ManagedThreadId);

}

執(zhí)行結(jié)果:

可以看出來(lái),直接用Thread會(huì)開(kāi)啟5個(gè)線(xiàn)程,用Task(用了線(xiàn)程池)開(kāi)啟了3個(gè)!

2.1 Task<TResult>

Task<TResult>就是有返回值的Task,TResult就是返回值類(lèi)型。

Console.WriteLine("主線(xiàn)程開(kāi)始");

//返回值類(lèi)型為string

Task<string> task = Task<string>.Run(() => {

    Thread.Sleep(2000);

    return Thread.CurrentThread.ManagedThreadId.ToString();

});

//會(huì)等到task執(zhí)行完畢才會(huì)輸出;

Console.WriteLine(task.Result);

Console.WriteLine("主線(xiàn)程結(jié)束");


運(yùn)行結(jié)果:

通過(guò)task.Result可以取到返回值,若取值的時(shí)候,后臺(tái)線(xiàn)程還沒(méi)執(zhí)行完,則會(huì)等待其執(zhí)行完畢!

簡(jiǎn)單提一下:

Task任務(wù)可以通過(guò)CancellationTokenSource類(lèi)來(lái)取消,感覺(jué)用得不多,用法比較簡(jiǎn)單,感興趣的話(huà)可以搜一下!

 3. async/await

async/await是C#5.0中推出的,先上用法:

static void Main(string[] args)

{

    Console.WriteLine("-------主線(xiàn)程啟動(dòng)-------");

    Task<int> task = GetStrLengthAsync();

    Console.WriteLine("主線(xiàn)程繼續(xù)執(zhí)行");

    Console.WriteLine("Task返回的值" + task.Result);

    Console.WriteLine("-------主線(xiàn)程結(jié)束-------");

}


static async Task<int> GetStrLengthAsync()

{

    Console.WriteLine("GetStrLengthAsync方法開(kāi)始執(zhí)行");

    //此處返回的<string>中的字符串類(lèi)型,而不是Task<string>

    string str = await GetString();

    Console.WriteLine("GetStrLengthAsync方法執(zhí)行結(jié)束");

    return str.Length;

}

static Task<string> GetString()

{

    //Console.WriteLine("GetString方法開(kāi)始執(zhí)行")  

    return Task<string>.Run(() => {

        Thread.Sleep(2000);

        return "GetString的返回值";

    });

}

async用來(lái)修飾方法,表明這個(gè)方法是異步的,聲明的方法的返回類(lèi)型必須為:void,Task或Task<TResult>。

await必須用來(lái)修飾Task或Task<TResult>,而且只能出現(xiàn)在已經(jīng)用async關(guān)鍵字修飾的異步方法中。通常情況下,async/await成對(duì)出現(xiàn)才有意義,

看看運(yùn)行結(jié)果:

可以看出來(lái),main函數(shù)調(diào)用GetStrLengthAsync方法后,在await之前,都是同步執(zhí)行的,直到遇到await關(guān)鍵字,main函數(shù)才返回繼續(xù)執(zhí)行。

那么是否是在遇到await關(guān)鍵字的時(shí)候程序自動(dòng)開(kāi)啟了一個(gè)后臺(tái)線(xiàn)程去執(zhí)行GetString方法呢?

現(xiàn)在把GetString方法中的那行注釋加上,運(yùn)行的結(jié)果是:

大家可以看到,在遇到await關(guān)鍵字后,沒(méi)有繼續(xù)執(zhí)行GetStrLengthAsync方法后面的操作,也沒(méi)有馬上反回到main函數(shù)中,而是執(zhí)行了GetString的第一行,以此可以判斷await這里并沒(méi)有開(kāi)啟新的線(xiàn)程去執(zhí)行GetString方法,而是以同步的方式讓GetString方法執(zhí)行,等到執(zhí)行到GetString方法中的Task<string>.Run()的時(shí)候才由Task開(kāi)啟了后臺(tái)線(xiàn)程!

那么await的作用是什么呢?

可以從字面上理解,上面提到task.wait可以讓主線(xiàn)程等待后臺(tái)線(xiàn)程執(zhí)行完畢,await和wait類(lèi)似,同樣是等待,等待Task<string>.Run()開(kāi)始的后臺(tái)線(xiàn)程執(zhí)行完畢,不同的是await不會(huì)阻塞主線(xiàn)程,只會(huì)讓GetStrLengthAsync方法暫停執(zhí)行。

那么await是怎么做到的呢?有沒(méi)有開(kāi)啟新線(xiàn)程去等待?

只有兩個(gè)線(xiàn)程(主線(xiàn)程和Task開(kāi)啟的線(xiàn)程)!至于怎么做到的(我也不知道......>_<),大家有興趣的話(huà)研究下吧!

4.IAsyncResult

IAsyncResult自.NET1.1起就有了,包含可異步操作的方法的類(lèi)需要實(shí)現(xiàn)它,Task類(lèi)就實(shí)現(xiàn)了該接口

在不借助于Task的情況下怎么實(shí)現(xiàn)異步呢?

class Program

{

    static void Main(string[] args)

    {

        Console.WriteLine("主程序開(kāi)始--------------------");

        int threadId;

        AsyncDemo ad = new AsyncDemo();

        AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

        IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null);

        Thread.Sleep(0);

        Console.WriteLine("主線(xiàn)程線(xiàn)程 {0} 正在運(yùn)行.", Thread.CurrentThread.ManagedThreadId)

            //會(huì)阻塞線(xiàn)程,直到后臺(tái)線(xiàn)程執(zhí)行完畢之后,才會(huì)往下執(zhí)行      

            result.AsyncWaitHandle.WaitOne();

        Console.WriteLine("主程序在做一些事情!!!");

        //獲取異步執(zhí)行的結(jié)果 

        string returnValue = caller.EndInvoke(out threadId, result);

        //釋放資源    

        result.AsyncWaitHandle.Close();

        Console.WriteLine("主程序結(jié)束--------------------");

        Console.Read();

    }

}

public class AsyncDemo

{

    //供后臺(tái)線(xiàn)程執(zhí)行的方法

    public string TestMethod(int callDuration, out int threadId)

    {

        Console.WriteLine("測(cè)試方法開(kāi)始執(zhí)行.");

        Thread.Sleep(callDuration);

        threadId = Thread.CurrentThread.ManagedThreadId;

        return String.Format("測(cè)試方法執(zhí)行的時(shí)間 {0}.", callDuration.ToString());

    }

}

public delegate string AsyncMethodCaller(int callDuration, out int threadId);

關(guān)鍵步驟就是紅色字體的部分,運(yùn)行結(jié)果:

和Task的用法差異不是很大!result.AsyncWaitHandle.WaitOne()就類(lèi)似Task的Wait。

 5.Parallel

最后說(shuō)一下在循環(huán)中開(kāi)啟多線(xiàn)程的簡(jiǎn)單方法:

Stopwatch watch1 = new Stopwatch();

watch1.Start();

    for (int i = 1; i <= 10; i++)

    {

    Console.Write(i + ",");

    Thread.Sleep(1000);

}

watch1.Stop();

Console.WriteLine(watch1.Elapsed);

Stopwatch watch2 = new Stopwatch();

watch2.Start();

//會(huì)調(diào)用線(xiàn)程池中的線(xiàn)程

Parallel.For(1, 11, i =>{ 

Console.WriteLine(i + ",線(xiàn)程ID:" + Thread.CurrentThread.ManagedThreadId); 

Thread.Sleep(1000);});

watch2.Stop();

Console.WriteLine(watch2.Elapsed);


運(yùn)行結(jié)果:

循環(huán)List<T>:

List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 6, 7, 8, 9 };

Parallel.ForEach<int>(list, n =>{ 

Console.WriteLine(n); 

Thread.Sleep(1000);});

執(zhí)行Action[]數(shù)組里面的方法:

Action[] actions = new Action[] { new Action(() => { Console.WriteLine("方法1"); }), new Action(() => { Console.WriteLine("方法2"); }) };

Parallel.Invoke(actions);


6.異步的回調(diào)

文中所有Task<TResult>的返回值都是直接用task.result獲取,這樣如果后臺(tái)任務(wù)沒(méi)有執(zhí)行完畢的話(huà),主線(xiàn)程會(huì)等待其執(zhí)行完畢,這樣的話(huà)就和同步一樣了(看上去一樣,但其實(shí)await的時(shí)候并不會(huì)造成線(xiàn)程的阻塞,web程序感覺(jué)不到,但是wpf,winform這樣的桌面程序若不使用異步,會(huì)造成UI線(xiàn)程的阻塞)。簡(jiǎn)單演示一下Task回調(diào)函數(shù)的使用:

Console.WriteLine("主線(xiàn)程開(kāi)始");

Task<string> task = Task<string>.Run(() => { Thread.Sleep(2000); return Thread.CurrentThread.ManagedThreadId.ToString(); });

//會(huì)等到任務(wù)執(zhí)行完之后執(zhí)行

task.GetAwaiter().OnCompleted(() =>{

Console.WriteLine(task.Result);});

Console.WriteLine("主線(xiàn)程結(jié)束");

Console.Read();

執(zhí)行結(jié)果:

OnCompleted中的代碼會(huì)在任務(wù)執(zhí)行完成之后執(zhí)行!

另外task.ContinueWith()也是一個(gè)重要的方法:

Console.WriteLine("主線(xiàn)程開(kāi)始");

Task<string> task = Task<string>.Run(() => {

    Thread.Sleep(2000);

    return Thread.CurrentThread.ManagedThreadId.ToString();

});

task.GetAwaiter().OnCompleted(() =>{ 

    Console.WriteLine(task.Result);

});

task.ContinueWith(m=>{

    Console.WriteLine("第一個(gè)任務(wù)結(jié)束啦!我是第二個(gè)任務(wù)");

});

Console.WriteLine("主線(xiàn)程結(jié)束");

Console.Read();

執(zhí)行結(jié)果:

ContinueWith()方法可以讓該后臺(tái)線(xiàn)程繼續(xù)執(zhí)行新的任務(wù)。

Task的使用還是比較靈活的,大家可以研究下,好了,以上就是全部?jī)?nèi)容了,篇幅和能力都有限,希望對(duì)大家有用!

 

出處:http://www.hzhcontrols.com/
原文:http://www.hzhcontrols.com/new-605059.html
本文版權(quán)歸原作者所有
歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利


該文章在 2024/1/3 23:59:27 編輯過(guò)
關(guān)鍵字查詢(xún)
相關(guān)文章
正在查詢(xún)...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專(zhuān)業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車(chē)隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類(lèi)企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷(xiāo)售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶(hù)的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved