Tại sao dùng async task maf k dùng thread
Lập trình bất đồng bộ asynchronousTừ .NET Framework 4.5 nó thêm
vào thư viện có tên Task Parallel Library (TPL) - TPL giúp lập trình chạy song song (đa luồng) dễ dàng hơn. Trong C# đồng thời nó thêm vào hai từ khóa là
async và await , đây là hai từ khóa chính để sử dụng trong lập trình bất đồng bộ. Show Lập trình bất đồng bộ (asynchronous) là một cách thức mà khi gọi nó chạy ở chế độ nền (liên quan đến một tiến trình, task), trong khi đó tiến trình gọi nó không bị khóa - block. Trong .NET có triển khai một số mô hình lập trình bất đồng bộ như Asynchronous pattern, mẫu bất đồng bộ theo sự kiện và theo tác vụ (TAP - task-based asynchronous pattern) Phần này sẽ nói về TAP - task-based asynchronous pattern - mộ hình lập trình bất đồng bộ thông dụng trên .NET hiện nay. Lập trình đồng bộ synchronousBình thường, khi lập trình gọi một phương thức nào đó thì phương thức đó chạy và kết thúc thì các dòng code tiếp theo sau lời gọi phương thức đó mới được thực thi, đó là chạy đồng bộ, có nghĩa là thread gọi phương thức bị khóa lại cho đến khi phương thức kết thúc.
Thử xem ví dụ đơn giản sau: DownloadWebsite01.cs using System; using System.Net; using System.Threading; namespace CS021_ASYNCHRONOUS { public class DownloadWebsite01 { public static string DownloadWebpage (string url, bool showresult) { using (var client = new WebClient ()) { Console.Write ("Starting download ..."); string content = client.DownloadString (url); Thread.Sleep (3000); if (showresult) Console.WriteLine (content.Substring (0, 150)); return content; } } public static void TestDownloadWebpage() { string url = "https://code.visualstudio.com/"; DownloadWebpage(url, true); Console.WriteLine("Do somthing ..."); } } } Hãy gọi Phương thức Khi chạy, thì lời gọi Vấn đề là khi Để giải quyết vấn đề này, trong khi chờ cho Lớp TaskLớp Cần sử dụng các namespace sau để có thư viện về Task using System.Threading; using System.Threading.Tasks; Chú ý hãy tìm hiểu về hàm ủy quyền delegate C# trước Cú pháp tạo ra đối tượng Task cơ bảnĐể tạo ra một Task bạn cần tham số là một hàm delegate ( Func hoặc Action), ví dụ delegate có tên
Để chạy Task gọi phương thức Ta sẽ làm một ví dụ sử dụng Task tạo ra các 2 tiến trình con chạy đồng thời: TestAsync01.cs using System; using System.Net; using System.Threading; using System.Threading.Tasks; namespace CS021_ASYNCHRONOUS { public class TestAsync01 { // Viết ra màn hình thông báo có màu public static void WriteLine (string s, ConsoleColor color) { Console.ForegroundColor = color; Console.WriteLine (s); } // Tạo và chạy Task, sử dụng delegate Func (có kiểu trả về) public static Task Chạy trong hàm main Console.WriteLine($"{' ',5} {Thread.CurrentThread.ManagedThreadId,3} MainThread"); Task Kết quả chạy Để khởi tạo một Lưu ý: sử dụng Func Sau đó tạo Task với cú pháp Task Khi đã có Task, gọi phương thức Khi
Task chạy xong, kết quả delegate trả về lưu ở thuộc tính Kết quả chạy code trên, hai thread con đang chạy song song, trong khi thread chính đàng chờ người người dùng bấm bàn phím. Cũng để ý Action action = () => {}; Task task = new Task(action); async và awaitMã trên bạn thấy, khi đối tượng
Task khởi chạy bằng Vấn đề là khi truy cập public static Task Khi Bạn thấy thread trong Vậy 2 task trong ASync1 và Async2 không được chạy đồng thời, task này kết thúc mới chạy được task kia => Lợi ích của đa luồng, bất đồng bộ mất đi. Giờ bạn mong muốn khi gọi Lúc này bạn cần sử dụng đến cặp từ khóa
Bước 1) Thêm vào khai báo tên hàm từ khóa async, nó cho trình biên dịch biết đây là hàm bất đồng bộ - khi gọi nó - nó trả về ngay lập tức public static async Task Bước 2) Trong thân của Async1, phải có đoạn code chờ task hoàn thành await task; // đây là điểm không khóa thread chính, thread chính chạy tiếp, task chạy tiếp
Với hàm async không cần kiểu trả về thì phải trả về Vậy hàm Async sau khi chuyển nó là hàm bất đồng bộ với từ khóa using System; using System.Net; using System.Threading; using System.Threading.Tasks; namespace CS021_ASYNCHRONOUS { public class TestAsyncAwait { // Viết ra màn hình thông báo có màu public static void WriteLine (string s, ConsoleColor color) { Console.ForegroundColor = color; Console.WriteLine (s); } // Tạo và chạy Task, sử dụng delegate Func (có kiểu trả về) public static async Task Chạy thử đoạn code trong hàm main static async Task Main(string[] args) { var t1 = TestAsyncAwait.Async1("x", "y"); var t2 = TestAsyncAwait.Async2(); // Làm gì đó khi t1, t2 đang chạy Console.WriteLine("Task1, Task2 đang chạy"); await t1; // chờ t1 kết thúc Console.WriteLine("Làm gì đó khi t1 kết thúc"); await t2; // chờ t2 kết thúc } Nhớ là trong hàm main để thi hành được tác vụ chờ task, gọi các phương thức bất đồng bộ, bạn cũng phải thiết lập main là bất đồng bộ như trên. Sau khi chạy, bạn sẽ thấy 2 task đã cùng chạy một lúc - và đoạn code sau Phương thức async trả về TaskKhi khai báo hàm với Cú pháp async Task Ví dụ khai báo hàm async trả về kiểu Nhìn lại hàm static async Task Do Async1 một Task, thì nó có thể static async Task Main(string[] args) { var t1 = TestAsyncAwait.Async1("x", "y"); //.. await t1; // chờ t1 kết thúc //... } Áp dùng xây dựng chức năng tải một file từ internet về có áp dụng kỹ thuật bất đồng bộ (async/await), dùng Mặc dù WebClient có sẵn phương thức đồng bộ, nhưng ở đây ta sẽ dùng phương thức Ban đầu để tải được file, dùng code đồng bộ thì sẽ như sau: public static void DownloadFile (string url) { using (var client = new WebClient ()) { Console.Write ("Starting download ..." + url); // mảng byte tải về byte[] data = client.DownloadData(new Uri(url)); // Lấy tên file để lưu string filename = System.IO.Path.GetFileName(url); System.IO.File.WriteAllBytes(filename, data); } } Giờ ta sẽ chuyển thành code đồng bộ, nó sẽ có dạng: DownloadAsync.cs using System; using System.Net; using System.Threading.Tasks; namespace CS021_ASYNCHRONOUS { public class DownloadAsync { public static async Task DownloadFile (string url) { Action downloadaction = () => { using (var client = new WebClient ()) { Console.Write ("Starting download ..." + url); // mảng byte tải về byte[] data = client.DownloadData(new Uri(url)); // Lấy tên file để lưu string filename = System.IO.Path.GetFileName(url); System.IO.File.WriteAllBytes(filename, data); } }; Task task = new Task(downloadaction); task.Start(); await task; Console.WriteLine("Đã hoàn thành tải file"); } } } Ta thấy toàn bố code của thân hàm khi ở trạng thái đồng bộ được đưa vào một Action (deleage) có tên Hàm Main gọi tải thử static async Task Main(string[] args) { string url = "https://github.com/microsoft/vscode/archive/1.48.0.tar.gz"; var taskdonload = DownloadAsync.DownloadFile(url); //.. Console.WriteLine("Làm gì đó khi file đang tải"); //.. await taskdonload; Console.WriteLine("Làm gì đó khi file tải xong"); } Hãy chạy thử để xem kết quả, nó tải về mã nguồn VSC từ github. Trong khi file đang tải ở thread chính làm việc gì đó tùy bạn Yêu cầu kết thúc Task đang thực thi với CancellationTokenKhi tạo ra các Task, còn có thể khởi tạo - truyền cho nó một đối tượng kiểu CancellationToken, đối tượng này được phát sinh bởi lớp CancellationTokenSource. Khi trong task có CancellationToken thì trong quá trình thực thi nó có thể kiểm tra Ví dụ: có task1, task2 đang chạy, nếu nhấn phím e thì 2 task này kết thúc static async Task Main(string[] args) { // Đối tượng để phát đi yêu cầu dừng Task var tokenSource = new CancellationTokenSource(); // Lấy token - để sử dụng bởi task, khi task thực thi // token.IsCancellationRequested là true nếu có phát yêu cầu dừng // bằng cách gọi tokenSource.Cancel var token = tokenSource.Token; // Tạo task1 có sử dụng CancellationToken Task task1 = new Task( () => { for (int i = 0; i < 10000; i++) { // Kiểm tra xem có yêu cầu dừng thì kết thúc task if (token.IsCancellationRequested) { Console.WriteLine("TASK1 STOP"); token.ThrowIfCancellationRequested(); return; } // Chạy tiếp Console.WriteLine("TASK1 runing ... " + i); Thread.Sleep(300); } }, token ); // Tạo task1 có sử dụng CancellationToken Task task2 = new Task( () => { for (int i = 0; i < 10000; i++) { if (token.IsCancellationRequested) { Console.WriteLine("TASK1 STOP"); token.ThrowIfCancellationRequested(); return; } Console.WriteLine("TASK2 runing ... " + i); Thread.Sleep(300); } }, token ); // Chạy các task task1.Start(); task2.Start(); while (true) { var c = Console.ReadKey().KeyChar; // Nếu bấm e sẽ phát yêu cầu dừng task if (c == 'e') { // phát yêu cầu dừng task tokenSource.Cancel(); break; } } Console.WriteLine("Các task đã kết thúc, bấm phím bất kỳ kết thúc chương trình"); Console.ReadKey(); } Lưu ý trong .NET hỗ trọng dừng task bằng cách dụng CancellationToken, nhưng không có sẵn các lớp hỗ trợ để PAUSE (tạm dừng) task đang chạy, cũng như RESUME (chạy tiếp) một task đang dừng. Nếu muốn có chức năng này bạn cần tự triển khai, hãy theo hướng dẫn tại pausing async methods để xây dựng ra các lớp PauseTokenSource, PauseToken Mã nguồn ví dụ CS021_ASYNCHRONOUS (git) hoặc tải về ex021 |