12/08/2018, 14:07

Lập trình bất đồng bộ trong C#

1. Giới thiệu về công nghệ lập trình Asynchronous trong C# Trong rất nhiều ngôn ngữ lập trình hiện nay, việc hỗ trợ lập trình bất đồng bộ(Asynchronous programing) đã trở nên khá phổ biến. Ví dụ thường gặp nhất là việc giao tiếp với server thông qua Ajax của javascript. Như vậy có thể hiểu ...

1. Giới thiệu về công nghệ lập trình Asynchronous trong C#

Synchronous-vs.-asynchronous.jpg

Trong rất nhiều ngôn ngữ lập trình hiện nay, việc hỗ trợ lập trình bất đồng bộ(Asynchronous programing) đã trở nên khá phổ biến. Ví dụ thường gặp nhất là việc giao tiếp với server thông qua Ajax của javascript. Như vậy có thể hiểu đơn giản lập trình bất đồng bộ (Asynchronous) là khả năng thực thi các tác vụ độc lập nhau, có nghĩa là chúng không nhất thiết phải chạy một cách tuần tự (có thể chạy song song với nhau) hoặc xử lý này không phải đợi xử lý khác giúp cho việc cải thiện hiệu suất hoạt động của ứng dụng. Trong C# bắt đầu thực thi từ phương thức Main và kết thúc khi phương thức Main được trả về. Trong đó tất cả các xử lý thực hiện một cách tuần tự hết cái này đến cái khác. Một xử lý phải đợi xử lý trước đó hoàn thành.

static void Main(string[] args)
{
    DoTaskOne();
    DoTaskTwo();
}

Ta có thể thấy. Với cách lập trình đồng bộ thì "DoTaskTwo" không thể bất đầu cho đến khi "DoTaskOne" hoàn thành. Trong lập trình bất đồng bộ phương thức được gọi sẽ được chạy trong background và việc gọi thread là không bị block. Sau khi gọi phương thức thực thi, luồng trở lại gọi và thực thi những tác vụ khác. Thông thường chúng sử dụng Thread hoặc Task.

Trong trường hợp của chúng ta, nếu chúng ta chạy "DoTaskOne" theo cách bất đồng bộ thì sau khi gọi "DoTaskOne" luồng thực thi sẽ ngay lập tức quay lại phương thức Main và bất đầu "DoTaskTwo"

Chúng ta có thể tạo thread bằng việc sử dụng Thread class hoặc sử dụng asynchronous patterns được cung cấp bởi .Net để thực hiện lập trình bất đồng bộ

2. Những điểm nổi bật của Asynchronous trong C#

2.1 Cải thiện hiệu suất ứng dụng

Lập trình bất đồng bộ thường được sử dụng trong các xử lý tiềm ẩn blocking, giống như truy cập tới website, việc truy cập tài nguyên trên web đôi lúc là chậm trễ. Ngoài ra có một số xử lý khác nên được xây dựng bất động bộ như: Làm việc trên file, ảnh,..

Từ .Net Framework 4.5 và Windows Runtime sẽ có một số phương thức trong các APIs hỗ trợ bất đồng bộ như ở dưới:

  • Web access: HttpClient, SyndicationClient
  • Working with files: StorageFile, StreamWriter, StreamReader, XmlReader
  • Working with images: MediaCapture, BitmapEncoder, BitmapDecoder
  • WCF programming: Synchronous and Asynchronous Operations

2.2 Phương thức bất đồng bộ trong C# dễ dàng để viết

Chúng ta có thể thông nhất rằng lập trình bất đồng bộ là rất tốt để cải thiện performance cho ứng dụng chúng ta. Tuy nhiên với công nghệ trước đây, việc viết ứng dụng bất đồng bộ là khá phức tạp, khó khăn để viết, debug và bảo trì.

Do vậy, .Net Framework 4.5 ra đời có một cách tiếp cập đơn giản để thúc đẩy lập trình bất đồng bộ trở nên dễ dàng hơn bao giờ hết. Những phần việc khó khăn nhất mà trước kia lập trình viên từng làm sẽ được thực hiện bởi compiler. Như vậy cấu trúc logic của ứng dụng sẽ vẫn giữ như lập trình đồng bộ thông thường.

Trong C#, hai từ khóa asyn và await là trung tâm của lập trình bất đồng bộ. Bằng việc sử dụng hai từ khóa này, bạn có thể sử dụng tài nguyên trong .Net hoặc Windows Runtime để tạo một phương thức bất đồng bộ một cách dễ dàng. Chúng ta cùng theo dõi ví dụ bên dưới:

async Task<int> AccessTheWebAsync()
{
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoIndependentWork();

    //  - The await operator then retrieves the string result from getStringTask.
    string urlContents = await getStringTask;

    // The return statement specifies an integer result.
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value.
    return urlContents.Length;
}

Gọi phương thức trên:

string urlContents = await client.GetStringAsync();

Hình ảnh bên dưới là luồng thực thi của phương thức 'GetStringAsync':

IC612215.jpeg

2.3 Awaiting nhiều phương thức bất đồng bộ

Trước tiên, bạn hãy quan sát code sau:

private async void CallWithAsync()
{
    string result = await DoTask1Async("Task1");
    string result1 = await DoTask2Async("Task2");
    Console.WriteLine(result);
    Console.WriteLine(result1);
}

Ở trên, chúng ta đợi hai phương thức bất đồng bộ một cách tuần tự. Việc gọi hàm thứ 2 sẽ bắt đầu sau khi công việc hàm đầu hoàn thành. Đây không phải là ý tưởng tốt trong thực tế nếu chúng không phụ thuộc vào kết quả của nhau bởi vì "DoTask1Async" có thể block chương trình. Để giải quyết vấn đề này trong C# chúng ta có thể sử dụng Task.WhenAll

private async static void MultipleAsyncMethodsWithCombinators()
{
     Task<string> t1 = DoTask1Async("Task1");

     Task<string> t2 = DoTask2Async("Task2");

     await Task.WhenAll(t1, t2);

    Console.WriteLine("Finished both methods.
 " +

    "Result 1: {0}
 Result 2: {1}", t1.Result, t2.Result);
}

Với code trên cả hai phương thức trên được thực thi một cách song song, không block lẫn nhau.

2.4 Canceling tác vụ

Trước đây, nếu chúng ta sử dụng Thread thì việc hủy tác vụ của nó là không thể. Từ Net 4.0 trở lên C# cung cấp một cách để hủy các Task đang thực thi dựa trên CancellationTokenSource class

static void Main(string[] args)
{
    CallWithAsync();
    Console.ReadKey();
}

async static void CallWithAsync()
{
    try
    {
        CancellationTokenSource source = new CancellationTokenSource();
        source.CancelAfter(TimeSpan.FromSeconds(1));
        var t1 = await GreetingAsync("HiepHV", source.Token);
    }
    catch (OperationCanceledException ex)
    {
        Console.WriteLine(ex.Message);
    }
}

static Task<string> GreetingAsync(string name, CancellationToken token)
{
    return Task.Run<string>(() =>
    {
        return Greeting(name, token);
    });
}

static string Greeting(string name, CancellationToken token)
{
    Thread.Sleep(3000);
    token.ThrowIfCancellationRequested();
    return string.Format("Hello, {0}", name);
}

Đây là một tính năng rất hay trong lập trình bất động của .Net. Trong ứng dụng thực thế, có những xử lý như đang upload file, xử lý document,.. và các xử lý bất động khác đôi khi rất khó để hủy tác vụ của nó khi đang thực hiện. CancellationTokenSource class có thể giúp chúng ta control điều này dễ dàng hơn.

2.5 Lập trình song song

Rất nhiều máy tính cá nhân và các máy trạm có hai hoặc bốn nhân của CPU, điều đó cho phép nhiều luồng xử lý thực hiện đồng thời. Trong tương lai gần, phần cứng máy tính sẽ ngày càng được nâng cấp(có nhiều nhân CPU hơn,..). Vì vậy để tận dụng những lợi thế của phần cứng hiện nay và sau này, bạn có thể làm song song hóa các tác vụ trong code thông qua nhiều trình xử lý. Trong quá khứ, lập trình song song hay còn gọi là đa luồng yêu cầu một sự nắm bắt ở mức low-level của thread và locks. VS 2010 và .Net 4.0 ra đời đã mở rộng hỗ trợ lập trình song song bằng việc cung cấp new runtime, new class library types và new diagnostic tools. Những tính năng này nhằm giúp đơn giản hóa việc phát triển lập trình song song. Hình ảnh bên dưới sẽ minh họa kiến trúc 'parallel programing' trong .Net 4.0:

IC292903.jpeg

Example code:

ParallelLoopResult result =
       Parallel.For(0, 100, async (int i) =>
       {
           Console.WriteLine("{0}, task: {1}, thread: {2}", i,
           Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
           await Task.Delay(10);

      });

3. Asynchronous và threading

Trước khi Asynchronous programing ra đời ở những phiên bản cũ hơn của .Net, chúng ta có công nghệ lập trình đa luồng(multiple thread). Theo hiểu biết của tôi, với mỗi thread chúng ta tạo ra trong code sẽ cần một thread tương ứng của CPU. Nó cũng thể coi là một dạng lập trình bất đồng bộ hay có thể gọi là một bộ phận của Asynchronous programing hiện nay. Với công nghệ Asynchronous trong .Net hiện nay, nó có thể chạy nhiều taks trong cùng một thread hoặc nhiều thread khác nhau tùy tình huống cự thể. Rõ ràng đây là một ưu điểm lớn giúp cải thiện hiệu suất tốt hơn cho ứng dụng so với sử 'dụng Multiple Thread'

4. Kết

Hầu hết các lập trình viên hiện nay đều khá quen thuộc với lập trình bất đồng bộ trong các ngôn ngữ khác nhau như: javascript, java, python,ruby..Trong bài viết này tôi muốn trình bày những hiểu biết của mình về lập trình bất đồng bộ trong C# và .Net. Hy vọng sẽ góp một góc nhìn có ích cho các bạn.

Tham khảo:

Asynchronous Programming with async and await (C#)

Asynchronous programming and Threading in C#

0