C#異步編程詳解
前言
本節(jié)主要介紹異步編程中Task、Async和Await的基礎(chǔ)知識(shí)。
什么是異步?
異步處理不用阻塞當(dāng)前線程來等待處理完成,而是允許后續(xù)操作,直至其它線程將處理完成,并回調(diào)通知此線程。
異步和多線程
相同點(diǎn):避免調(diào)用線程阻塞,從而提高軟件的可響應(yīng)性。
不同點(diǎn):
異步操作無須額外的線程負(fù)擔(dān),并且使用回調(diào)的方式進(jìn)行處理,在設(shè)計(jì)良好的情況下,處理函數(shù)可以不必使用共享變量(即使無法完全不用,最起碼可以減少 共享變量的數(shù)量),減少了死鎖的可能。C#5.0 .NET4.5 以后關(guān)鍵字Async和Await的使用,使得異步編程變得異常簡(jiǎn)單。
多線程中的處理程序依然是順序執(zhí)行,但是多線程的缺點(diǎn)也同樣明顯,線程的使用(濫用)會(huì)給系統(tǒng)帶來上下文切換的額外負(fù)擔(dān)。并且線程間的共享變量可能造成死鎖的出現(xiàn)。
異步應(yīng)用場(chǎng)景及原理
異步主要應(yīng)用于IO操作,數(shù)據(jù)庫(kù)訪問,磁盤操作,Socket訪問、HTTP/TCP網(wǎng)絡(luò)通信
原因:對(duì)于IO操作并不需要CPU進(jìn)行過多的計(jì)算,這些數(shù)據(jù)主要通過磁盤進(jìn)行處理,如果進(jìn)行同步通信無法結(jié)束,需要?jiǎng)?chuàng)建更多的線程資源,線程的數(shù)據(jù)上下文頻繁的切換也是對(duì)資源的浪費(fèi),針對(duì)IO操作不需要單獨(dú)的分配一個(gè)線程來處理。
舉例說明:
操作:服務(wù)器接收HTTP請(qǐng)求對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作然后返回
同步處理請(qǐng)求的線程會(huì)被阻塞,異步處理請(qǐng)求的線程不會(huì)阻塞。

任務(wù)
在使用任務(wù)之前,針對(duì)線程的調(diào)用大多都用線程池提供的靜態(tài)方法QueueUserWorkItem,但是這個(gè)函數(shù)有很多的限制,其中最大的問題就是沒有內(nèi)部機(jī)制可以讓開發(fā)者知道操作在什么時(shí)候完成,也沒有機(jī)制在操作完成時(shí)獲取返回值,微軟為了解決這個(gè)問題引入了任務(wù)的概念。
首先構(gòu)造一個(gè)Task<TResult>對(duì)象,并為TResult傳遞返回值,開始任務(wù)之后等待它并回去結(jié)果,示例代碼:
static void Main(string[] args)
{
Console.WriteLine("開始進(jìn)行計(jì)算");
// ThreadPool.QueueUserWorkItem(Sum, 10);
Task<int> task = new Task<int>(Sum, 100);
task.Start();
//顯示等待獲取結(jié)果
task.Wait();
//調(diào)用Result時(shí),等待返回結(jié)果
Console.WriteLine("程序結(jié)果為 Sum = {0}",task.Result);
Console.WriteLine("程序結(jié)束");
Console.ReadLine();
}
public static int Sum(object i)
{
var sum = 0;
for (var j = 0; j <= (int) i; j++)
{
Console.Write("{0} + ",sum);
sum += j;
}
Console.WriteLine( " = {0}",sum);
return sum;
}
除了wait等待單個(gè)任務(wù)外,task還提供了等待多個(gè)任務(wù),WaitAny和WaitAll,它阻止調(diào)用線程,直到數(shù)組中所有的Task對(duì)象完成。
取消任務(wù)
任務(wù)的取消同樣使用的是.NET Framework的標(biāo)準(zhǔn)取消操作模式,首先需要?jiǎng)?chuàng)建一個(gè)CancellationTokenSource對(duì)象,然后在函數(shù)中加入?yún)?shù)CancellationToken,將CancellationTokenSource的Token傳遞給方法,然后調(diào)用IsCancellationRequested獲取是否已經(jīng)取消該值進(jìn)行判斷。
static void Main(string[] args)
{
Console.WriteLine("開始進(jìn)行計(jì)算");
// ThreadPool.QueueUserWorkItem(Sum, 10);
var ctx = new CancellationTokenSource();
var task = new Task<int>(() => Sum(ctx.Token, 100000));
task.Start();
//顯示等待獲取結(jié)果
//task.Wait(ctx.Token);
Thread.Sleep(1000);
ctx.Cancel();
//調(diào)用Result時(shí),等待返回結(jié)果
Console.WriteLine("程序結(jié)果為 Sum = {0}", task.Result);
Console.WriteLine("程序結(jié)束");
Console.ReadLine();
}
public static int Sum(CancellationToken cts, object i)
{
var sum = 0;
for (var j = 0; j <= (int)i; j++)
{
if (cts.IsCancellationRequested) return sum;
Thread.Sleep(50);
Console.Write("{0} + ", sum);
sum += j;
}
Console.WriteLine(" = {0}", sum);
return sum;
}
任務(wù)完成后自動(dòng)啟動(dòng)新任務(wù)
實(shí)際的開發(fā)應(yīng)用中,經(jīng)常出現(xiàn)一次任務(wù)完成后立刻啟動(dòng)另外一個(gè)任務(wù),并且不能夠使線程阻塞,在任務(wù)尚未完成時(shí)調(diào)用result會(huì)使程序阻塞,無法查看任務(wù)的執(zhí)行進(jìn)度,TASK提供了一個(gè)方法ContinueWith,它不會(huì)阻塞任何線程,當(dāng)?shù)谝粋€(gè)任務(wù)完成時(shí),會(huì)立即啟動(dòng)第二個(gè)任務(wù)。
static void Main(string[] args)
{
Console.WriteLine("開始進(jìn)行計(jì)算");
// ThreadPool.QueueUserWorkItem(Sum, 10);
var ctx = new CancellationTokenSource();
var task = new Task<int>(() => Sum(ctx.Token, 100000));
task.Start();
var cwt = task.ContinueWith(p =>
{
Console.WriteLine("task result ={0} ",task.Result);
});
//顯示等待獲取結(jié)果
//task.Wait(ctx.Token);
Thread.Sleep(1000);
ctx.Cancel();
//調(diào)用Result時(shí),等待返回結(jié)果
Console.WriteLine("程序結(jié)果為 Sum = {0}", task.Result);
Console.WriteLine("程序結(jié)束");
Console.ReadLine();
}
public static int Sum(CancellationToken cts, object i)
{
var sum = 0;
for (var j = 0; j <= (int)i; j++)
{
if (cts.IsCancellationRequested) return sum;
Thread.Sleep(50);
Console.Write("{0} + ", sum);
sum += j;
}
Console.WriteLine(" = {0}", sum);
return sum;
}
Async&Await 簡(jiǎn)單使用
使用Async&Await的主要目的是方便進(jìn)行異步操作,因?yàn)?net 4.0 以前進(jìn)行異步操作時(shí)比較復(fù)雜的,主要是通過調(diào)用微軟提供的異步回調(diào)方法進(jìn)行編程,如果遇到需要自己實(shí)現(xiàn)的方法顯得非常頭疼,.net的各個(gè)版本都有自己主推的技術(shù),像.NET1.1中的委托,.NET2.0中的泛型,.NET3.0中的Linq,.NET4.0中的Dynimac,.net4.5主推的就是異步編程,大家只需要了解TASK+異步函數(shù)就可以實(shí)現(xiàn)異步編程。
async:告訴CLR這是一個(gè)異步函數(shù)。
await:將Task<TResult>返回值的函數(shù)進(jìn)行異步處理。
示例目的:獲取網(wǎng)址JS代碼,并在界面顯示。
private static async Task<string> DownloadStringWithRetries(string uri)
{
using (var client = new HttpClient())
{
// 第1 次重試前等1 秒,第2 次等2 秒,第3 次等4 秒。
var nextDelay = TimeSpan.FromSeconds(1);
for (int i = 0; i != 3; ++i)
{
try
{
return await client.GetStringAsync(uri);
}
catch
{
}
await Task.Delay(nextDelay);
nextDelay = nextDelay + nextDelay;
}
// 最后重試一次,以便讓調(diào)用者知道出錯(cuò)信息。
return await client.GetStringAsync(uri);
}
}
static void Main(string[] args)
{
Console.WriteLine("獲取百度數(shù)據(jù)");
ExecuteAsync();
Console.WriteLine("線程結(jié)束");
Console.ReadLine();
}
public static async void ExecuteAsync()
{
string text = await DownloadStringWithRetries("http://wwww.baidu.com");
Console.WriteLine(text);
}
運(yùn)行結(jié)果發(fā)現(xiàn),首先獲取百度數(shù)據(jù),線程結(jié)束,最后顯示HTML代碼,這是因?yàn)楫惒介_啟了新的線程,并不會(huì)造成線程阻塞。
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
C#實(shí)現(xiàn)軟件監(jiān)控外部程序運(yùn)行狀態(tài)的方法
這篇文章主要介紹了C#實(shí)現(xiàn)軟件監(jiān)控外部程序運(yùn)行狀態(tài)的方法,可實(shí)現(xiàn)監(jiān)控另一個(gè)程序的運(yùn)行狀態(tài)及觸發(fā)相應(yīng)事件的功能,是非常實(shí)用的技巧,需要的朋友可以參考下2014-12-12
C#中多維數(shù)組[,]和交錯(cuò)數(shù)組[][]的區(qū)別
這篇文章介紹了C#中多維數(shù)組[,]和交錯(cuò)數(shù)組[][]的區(qū)別,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01
C# string轉(zhuǎn)unicode字符的實(shí)現(xiàn)
本文主要介紹了C# string轉(zhuǎn)unicode字符的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02
使用xmltextreader對(duì)象讀取xml文檔示例
這篇文章主要介紹了使用xmltextreader對(duì)象讀取xml文檔的示例,需要的朋友可以參考下2014-02-02
Unity使用EzySlice實(shí)現(xiàn)模型多邊形順序切割
這篇文章主要為大家詳細(xì)介紹了Unity使用EzySlice實(shí)現(xiàn)模型多邊形順序切割,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07
unity實(shí)現(xiàn)車方向盤轉(zhuǎn)動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了unity實(shí)現(xiàn)車方向盤轉(zhuǎn)動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04
C#批量插入數(shù)據(jù)到sqlserver的方法詳解
這篇文章主要為大家詳細(xì)介紹了C#中四種可以批量插入數(shù)據(jù)到sqlserver的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下2025-02-02

