日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#并行编程(5):需要知道的异步

發(fā)布時間:2023/12/4 C# 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#并行编程(5):需要知道的异步 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

異步與并行的聯(lián)系

大家知道“并行”是利用CPU的多個核心或者多個CPU同時執(zhí)行不同的任務(wù),我們不關(guān)心這些任務(wù)之間的依賴關(guān)系。
但是在我們實際的業(yè)務(wù)中,很多任務(wù)之間是相互影響的,比如統(tǒng)計車間全年產(chǎn)量的運算要依賴于各月產(chǎn)量的統(tǒng)計結(jié)果。假如你想在計算月產(chǎn)量的時候做些其他事情,如導(dǎo)出生產(chǎn)異常報表,“異步”就可以登上舞臺了。

說到異步,必須要先提一下同步。一圖勝千言:

圖中操作C的執(zhí)行依賴B的結(jié)果,B的執(zhí)行依賴A的結(jié)果。線程1連續(xù)執(zhí)行操作A、B、C便是一個同步過程;相對地,線程1執(zhí)行完A后把結(jié)果給線程2,線程2開始執(zhí)行B,完成后把B的結(jié)果通知到線程1,線程1開始執(zhí)行C,線程1在等待操作B結(jié)果的時候執(zhí)行了D,這就是一個異步的過程;此外,異步過程中,B和D是并行執(zhí)行的。

并行會提高業(yè)務(wù)的執(zhí)行效率,但異步不會,異步甚至?xí)下龢I(yè)務(wù)的執(zhí)行,比如上面A->B->C的執(zhí)行過程。異步是讓等待變得更有價值,這種價值則體現(xiàn)在多個業(yè)務(wù)的并行上

C#中的異步

在需要長時間等待的地方都可以使用異步,比如讀寫文件、訪問網(wǎng)絡(luò)或者處理圖片。特別是在UI線程中,我們要保持界面的響應(yīng)性,耗時的操作最好都使用異步的方式執(zhí)行。

.NET提供了三種異步模式:

  • IAsyncResult模式(APM)

  • 基于事件的異步模式(EAP)

  • 基于任務(wù)的異步模式(TAP)

其中基于任務(wù)的異步模式是.NET推薦的異步編程方式。

IAsyncResult異步模式APM

下面是IAsyncResult基于委托的用法。





private delegate void AsyncWorkCaller(int workNo);

public static void Run()
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} will do some work.");
AsyncWorkCaller caller = DoWork;
AsyncCallback callback = ar =>
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} did the callback. [{ar.AsyncState}]");
};
IAsyncResult result = caller.BeginInvoke(1, callback, "callback msg");
DoWork(2);

caller.EndInvoke(result);
DoWork(3);
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} done the work.");
}





private static void DoWork(int workNo)
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> work #{workNo} started with thread #{Thread.CurrentThread.ManagedThreadId}.");
Thread.Sleep(1000);
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> work #{workNo} done with thread #{Thread.CurrentThread.ManagedThreadId}.");
}

我們使用BeginInvoke來異步執(zhí)行作業(yè)1,同時可以執(zhí)行作業(yè)2,調(diào)用EndInvoke的時候,當(dāng)前線程被阻塞直到作業(yè)1完成。我們也可以使用result.AsyncWaitHandle.WaitOne()來等待異步作業(yè)完成,同樣會阻塞當(dāng)前線程。此外,可以為異步作業(yè)增加回調(diào),異步作業(yè)在完成時會執(zhí)行回調(diào)函數(shù)。

基于事件的異步模式EAP

事件大家不會陌生,我們在Winform編程的時候,總會用到事件。下面是利用BackgroundWorker實現(xiàn)的一個基于事件的簡單異步過程。我們給異步對象(這里是BackgroundWorker)訂閱DoWork和RunWorkCompleted事件,當(dāng)調(diào)用RunWorkerAsync時,觸發(fā)異步對象的工作事件,此時會開辟一個新線程來執(zhí)行目標操作。目標操作完成時,觸發(fā)工作完成事件,執(zhí)行后續(xù)操作。與IAsyncResult模式不同的是,作業(yè)完成后的后續(xù)操作會在另外的一個線程執(zhí)行,而IAsyncResult模式中,完成回調(diào)會在目標操作的執(zhí)行線程中執(zhí)行。

public static class EventBasedAsync
{
private static readonly BackgroundWorker worker = new BackgroundWorker();

static EventBasedAsync()
{
worker.DoWork += Worker_DoWork;
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
}

public static void Run()
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} will do some work.");
worker.RunWorkerAsync(1);
DoWork(2);
DoWork(3);
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} done the work.");
}

private static void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} did something when work completed.");
}

private static void Worker_DoWork(object sender, DoWorkEventArgs e)
{
DoWork((int)e.Argument);
}


private static void DoWork(int workNo)
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> work #{workNo} started with thread #{Thread.CurrentThread.ManagedThreadId}.");
Thread.Sleep(3000);
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> work #{workNo} done with thread #{Thread.CurrentThread.ManagedThreadId}.");
}
}

實際上,我們可以利用AsyncOperationManager實現(xiàn)自己的異步對象,可以使用dnSpy對BackgroundWorker進行反編譯觀察具體的實現(xiàn)過程。

基于任務(wù)的異步模式TAP

在《C#并行編程(4):基于任務(wù)的并行》中,我們已經(jīng)總結(jié)過Task和Task<T>的用法,這里主要關(guān)注的是C#的async/await語法與Task的結(jié)合用法。

在C#中,我們使用async標記定義一個異步方法,使用await來等待一個異步操作。簡單的用法如下:

public async Task DoWorkAsync()
{
await Task.Delay(1000);
}

public async Task<int> DoWorkAndGetResultAsync()
{
await Task.Delay(1000);
return 1;
}

用async/await編寫異步過程很方便,但異步方法的執(zhí)行過程是怎樣呢?下面的例子展示了一個異步操作的調(diào)用過程,我們以這個例子來分析異步方法的調(diào)用過程。

public static async Task Run()
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} will do some work.");

Task workTask1 = DoWork(1);

Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} got task #{workTask1.Id} by async call.");

Task workTask2 = DoWork(2);
await workTask2;
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} got task #{workTask2.Id} by async call.");

Task workTask3 = DoWork(3);
await workTask3;
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} got task #{workTask3.Id} by async call.");

Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> thread #{Thread.CurrentThread.ManagedThreadId} done the work.");
}






private static async Task DoWork(int workNo)
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> work #{workNo} started with thread #{Thread.CurrentThread.ManagedThreadId}.");
DateTime now = DateTime.Now;
await Task.Run(() =>
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> work #{workNo} was running by task #{Task.CurrentId} with thread #{Thread.CurrentThread.ManagedThreadId}.");
while (now.AddMilliseconds(3000) > DateTime.Now)
{
}
});
Console.WriteLine($"{DateTime.Now:HH:mm:ss.ffffff}=> work #{workNo} done with thread #{Thread.CurrentThread.ManagedThreadId}.");
}

先來看一下例子的輸出:

19:07:33.032779=>?thread?#10?will?do?some?work.
19:07:33.039762=>?work?#1?started?with?thread?#10.
19:07:33.075664=>?thread?#10?got?task?#2?by?async?call.
19:07:33.075664=>?work?#2?started?with?thread?#10.
19:07:33.078658=>?work?#2?was?running?by?task?#3?with?thread?#11.
19:07:33.082647=>?work?#1?was?running?by?task?#1?with?thread?#6.
19:07:36.040739=>?work?#1?done?with?thread?#6.
19:07:36.077638=>?work?#2?done?with?thread?#11.
19:07:36.077638=>?thread?#11?got?task?#4?by?async?call.
19:07:36.077638=>?work?#3?started?with?thread?#11.
19:07:36.077638=>?thread?#11?got?task?#7?by?async?call.
19:07:36.077638=>?thread?#11?done?the?work.
19:07:36.077638=>?work?#3?was?running?by?task?#6?with?thread?#12.
19:07:39.077652=>?work?#3?done?with?thread?#12.

在上面的輸出中,我們單看work #1,它由thread #10啟動,計算過程在thread #6中執(zhí)行并結(jié)束,最后任務(wù)在thread #10中返回,這里我們沒有使用await來等待work?#1的異步任務(wù);假如我們使用await等待異步任務(wù),如work?#2,它在thread?#10中啟動,計算過程在thread?#11中執(zhí)行并結(jié)束,任務(wù)最后在thread?#11中返回。大家可能發(fā)現(xiàn)了兩者的不同:await改變了Run()方法的執(zhí)行線程,從DoWork()方法的執(zhí)行也能夠看出,await會改變異步方法的執(zhí)行線程!

實際上,編譯器會把異步方法轉(zhuǎn)換成狀態(tài)機結(jié)構(gòu),執(zhí)行到await時,編譯器把當(dāng)前正在執(zhí)行方法(任務(wù))掛起,當(dāng)await的任務(wù)執(zhí)行完成時,編譯器再恢復(fù)掛起的方法,所以我們的輸出中,異步方法await前面和后面的代碼,一般是在不同的線程中執(zhí)行的。編譯器通過這種狀態(tài)機的機制,使得等待異步操作的過程中線程不再阻塞,進而增強響應(yīng)性和線程利用率。

理解異步方法的執(zhí)行機制后,相信對異步的應(yīng)用會變得更加嫻熟,這里就不再總結(jié)異步的具體用法。

原文地址:https://www.cnblogs.com/chenbaoshun/p/10671403.html

.NET社區(qū)新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?

總結(jié)

以上是生活随笔為你收集整理的C#并行编程(5):需要知道的异步的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。