async await task
首先搞清楚什么是同步什么是異步
Synchronize 同步
asynchronous 異步
相差也就是一個(gè)a,也可以理解為a開頭的就是異步操作,
同步一般是:當(dāng)一個(gè)方法被調(diào)用時(shí),調(diào)用者需要等待該方法執(zhí)行完畢并返回才能繼續(xù)執(zhí)行
異步一般是:方法被調(diào)用時(shí)立即返回,并獲取一個(gè)線程執(zhí)行該方法內(nèi)部的業(yè)務(wù),調(diào)用者不用等待該方法執(zhí)行完畢
哲學(xué)上思考,同步更規(guī)則性,代碼會(huì)很好的順序執(zhí)行一般不會(huì)出錯(cuò)。異步必然伴隨著各種各樣的情況,但是很明顯在一些耗時(shí)操作的時(shí)候能幫助減少時(shí)間
以C#為例子進(jìn)行具體的學(xué)習(xí):C#中三個(gè)關(guān)鍵詞并不是同時(shí)發(fā)布的,task最先在dotnet中使用,可見先搞懂task是很重要的
TASK
task建立在線程池的基礎(chǔ)上,線程池也就是管理一堆線程的一東西,為什么需要進(jìn)行管理呢,需要的時(shí)候創(chuàng)建不就行了嗎?這里涉及到操作系統(tǒng),創(chuàng)建一個(gè)線程帶來的開銷遠(yuǎn)大于保持一個(gè)線程需要的時(shí)候再用的開銷大,而線程池并不是很高大上,功能僅僅是保存和分配線程這點(diǎn)功能,只用線程池不能控制線程池中線程的執(zhí)行順序,也不能獲取線程池內(nèi)線程取消/異常/完成的通知。
task可以說是一個(gè)線程池pro max,他添加了這些功能,但也是一次只能運(yùn)行一個(gè)函數(shù)。
使用的的辦法有3種
一:new一個(gè)新的task,當(dāng)想要使用的時(shí)候myNewTask.Start()
實(shí)例如下
Task task = new Task(() =>
{
Thread.Sleep(100);
Console.WriteLine($"hello, task1的線程ID為{Thread.CurrentThread.ManagedThreadId}");
});
task.Start();
二:創(chuàng)建并開啟一個(gè)task,Task.Factory.NewStart()
實(shí)例如下
Task task2 = Task.Factory.StartNew(() =>
{
Thread.Sleep(100);
Console.WriteLine($"hello, task2的線程ID為{ Thread.CurrentThread.ManagedThreadId}");
});
三:Task.Run() 將任務(wù)放在線程池隊(duì)列,返回并啟動(dòng)一個(gè)Task
Task task3 = Task.Run(() =>
{
Thread.Sleep(100);
Console.WriteLine($"hello, task3的線程ID為{ Thread.CurrentThread.ManagedThreadId}");
});
Console.WriteLine("執(zhí)行主線程!");
Console.ReadKey();
這是沒有返回值的例子,有返回值的也很簡(jiǎn)單
比如
Task
{
return $"hello, task1的ID為{Thread.CurrentThread.ManagedThreadId}";
});
task.Start();
Console.WriteLine(task.Result);
思考一下,可以知道,有返回值的情況下,獲取result必然是會(huì)阻塞主線程的,不阻塞會(huì)引發(fā)會(huì)亂
另外提一嘴 ,有種奇怪的運(yùn)行方式task.RunSynchronously();,這個(gè)會(huì)同步運(yùn)行一個(gè)task。
在使用Tread的時(shí)候,一般使用Join來阻塞主線程,類似的,Task也有這類功能,并且還更加完善static void Main(string[] args)
{
Task task1 = new Task(() => {
Thread.Sleep(500);
Console.WriteLine("線程1執(zhí)行完畢!");
});
task1.Start();
Task task2 = new Task(() => {
Thread.Sleep(1000);
Console.WriteLine("線程2執(zhí)行完畢!");
});
task2.Start();
//阻塞主線程。task1,task2都執(zhí)行完畢再執(zhí)行主線程
//執(zhí)行【task1.Wait();task2.Wait();】可以實(shí)現(xiàn)相同功能
Task.WaitAll(new Task[]{ task1,task2});
Console.WriteLine("主線程執(zhí)行完畢!");
Console.ReadKey();
}
Task也有類似的操作用來阻塞主線程:
一 waitAll
Task.WaitAll(new Task[]{ task1,task2});
這個(gè)函數(shù)接受一個(gè)Task數(shù)組,會(huì)一直阻塞直到里面的都執(zhí)行完畢
二 wait
上面的例子中也可以寫作
task1.Wait();
task2.Wait();
三 WaitAny
函數(shù)用法和一中的類似,不過是只要任意一個(gè)完成就會(huì)停止阻塞,用處也挺大但沒這么大
管道
很多語言中都有相通的思想,比如golang 的管道channel,在C#異步編程中也有體現(xiàn)
CancellationTokenSource這個(gè)類用來控制一個(gè)Task什么時(shí)候停止
舉例來說
CancellationTokenSource source = new CancellationTokenSource();
int index = 0;
//開啟一個(gè)task執(zhí)行任務(wù)
Task task1 = new Task(() =>
{
while (!source.IsCancellationRequested)
{
Thread.Sleep(1000);
Console.WriteLine($"第{++index}次執(zhí)行,線程運(yùn)行中...");
}
});
task1.Start();
//五秒后取消任務(wù)執(zhí)行
Thread.Sleep(5000);
//source.Cancel()方法請(qǐng)求取消任務(wù),IsCancellationRequested會(huì)變成true
source.Cancel();
在使用winform的時(shí)候,經(jīng)常會(huì)遇到修改ui的操作,這個(gè)時(shí)候必須使用異步才能不阻塞ui線程,不然會(huì)讓應(yīng)用好像“卡了”一樣,
二 await
首先需要說明清楚,Task是一個(gè)類,其子類有一個(gè)Result。并且Task不能像其他類一樣簡(jiǎn)單的理解為一個(gè)有很多方法的結(jié)構(gòu)體,我更傾向于將這個(gè)理解為一個(gè)“操作”,其內(nèi)部有很多普通類沒有的線程操作。一定要在前面加上await來調(diào)用,這個(gè)和函數(shù)很不一樣
我認(rèn)為這是一種比較好的調(diào)用范式
static async Task Main(string[] args)
{
// 開始異步讀取文件
Task
// 在讀取文件期間處理用戶輸入
Console.WriteLine("請(qǐng)輸入您的名字:");
string userName = Console.ReadLine();
Console.WriteLine($"您好,{userName}!文件正在讀取中,請(qǐng)稍等。");
// 在讀取文件期間執(zhí)行計(jì)算任務(wù)
Task<int> calculationTask = PerformSomeCalculationAsync();
// 等待文件讀取完成
string content = await fileReadTask;
Console.WriteLine("文件內(nèi)容讀取完成:");
Console.WriteLine(content);
// 等待計(jì)算任務(wù)完成
int calculationResult = await calculationTask;
Console.WriteLine($"計(jì)算任務(wù)完成,結(jié)果是:{calculationResult}");
Console.ReadKey();
}
// 異步讀取文件內(nèi)容
async static Task<string> GetContentAsync(string filename)
{
using (FileStream fs = new FileStream(filename, FileMode.Open))
{
var bytes = new byte[fs.Length];
Console.WriteLine("開始讀取文件");
int len = await fs.ReadAsync(bytes, 0, bytes.Length);
string result = Encoding.UTF8.GetString(bytes);
return result;
}
}
// 模擬一個(gè)異步計(jì)算任務(wù)
async static Task<int> PerformSomeCalculationAsync()
{
Console.WriteLine("開始計(jì)算任務(wù)...");
await Task.Delay(3000); // 模擬一個(gè)耗時(shí)的計(jì)算
Console.WriteLine("計(jì)算任務(wù)進(jìn)行中...");
return 42; // 假設(shè)計(jì)算結(jié)果是42
}
在這個(gè)例子中能像同步編程一樣使用異步編程,只需要多聲明一下Task和await,async
這背后有一個(gè)復(fù)雜的任務(wù)調(diào)度和上下文恢復(fù)機(jī)制,dotnet是很復(fù)雜的,我作為C# 的初學(xué)者并沒有能夠具體搞懂其中的奧妙,但是我可以簡(jiǎn)單的理解為,定義了Task之后,可以由await進(jìn)行調(diào)用,并且與之相關(guān)的一系列代碼都會(huì)進(jìn)入一個(gè)新的線程中完成,主線程會(huì)繞過與之相關(guān)的代碼,去完成接下來的任務(wù),至于async,很遺憾我也不能解釋清楚,但是顯然的是,使用了這一套方法的函數(shù)和普通函數(shù)相當(dāng)不一樣,內(nèi)部有一套自己的處理邏輯,async更像是告訴編譯器的一種聲明("你對(duì)我要特殊對(duì)待")
浙公網(wǎng)安備 33010602011771號(hào)