Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

Asynchronous Programming ใน C# ด้วย Async และ Await

ทำไมต้องใช้ Asynchronous Programming?

ใน Synchronous programming การทำงานที่ block (เช่น I/O, Network, Database) จะทำให้ Thread หยุดรอจนเสร็จสิ้น ทำให้ Application ไม่ตอบสนอง (Not Responding) และบर्बัดทรัพยากร

Asynchronous Programming อนุญาตให้ Thread ไปทำงานอื่นต่อได้ ขณะรอการทำงานที่ block เสร็จสิ้น เพิ่มประสิทธิภาพและตอบสนอง (Responsiveness)

คีย์เวิร์ด async และ await

async

  • ประกาศว่าเมธอดเป็น asynchronous
  • เมธอดที่มี async เรียกว่า async method
  • Return type ต้องเป็น Task, Task<T>, ValueTask<T>, หรือ void (สำหรับ event handlers)

await

  • หยุดการทำงานของเมธอด async ชั่วคราว จนกว่า Task จะเสร็จ
  • Thread จะถูกคืนให้ ThreadPool ไปทำงานอื่น
  • เมื่อ Task เสร็จ จะกลับมาทำงานต่อจากจุด await

ตัวอย่างเปรียบเทียบ

❌ Synchronous (Block UI)

private void OnRequestDownload(object sender, RoutedEventArgs e)
{
    var req = HttpWebRequest.Create(_requestedUri);
    var res = req.GetResponse(); // Block UI thread จนกว่าจะดาวน์โหลดเสร็จ
    // UI ค้าง ไม่ตอบสนอง
}

✅ Asynchronous (Non-blocking)

private async void OnRequestDownload(object sender, RoutedEventArgs e)
{
    var req = HttpWebRequest.Create(_requestedUri);
    var res = await req.GetResponseAsync(); // Plug async, UI ยังตอบสนอง
    // ทำงานต่อเมื่อดาวน์โหลดเสร็จ
}

โครงสร้าง Async Method

// Return Task (ไม่มีค่าคืน)
public async Task DoWorkAsync()
{
    await SomeAsyncOperation();
}

// Return Task<T> (มีค่าคืน)
public async Task CalculateAsync()
{
    int result = await LongRunningCalculation();
    return result;
}

// void เฉพาะ Event Handlers
public async void Button_Click(object sender, EventArgs e)
{
    await DoSomethingAsync();
}

ตัวอย่างจริง: ดาวน์โหลดเว็บเพจ

using System;
using System.Net.Http;
using System.Threading.Tasks;

public class WebDownloader
{
    private static readonly HttpClient client = new HttpClient();
    
    public async Task DownloadPageAsync(string url)
    {
        try
        {
            string content = await client.GetStringAsync(url);
            return content;
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            return null;
        }
    }
    
    public async Task DownloadMultipleAsync(string[] urls)
    {
        // รันพร้อมกัน (Parallel)
        var tasks = urls.Select(url => DownloadPageAsync(url));
        var results = await Task.WhenAll(tasks);
        
        for (int i = 0; i < urls.Length; i++)
        {
            Console.WriteLine($"{urls[i]}: {results[i]?.Length ?? 0} chars");
        }
    }
}

// ใช้งาน
public class Program
{
    public static async Task Main()
    {
        var downloader = new WebDownloader();
        string html = await downloader.DownloadPageAsync("https://example.com");
        Console.WriteLine($"Downloaded: {html.Length} characters");
    }
}

Task Parallel Library (TPL) - รันพร้อมกัน

// รันหลาย Task พร้อมกัน
Task task1 = DownloadAsync(url1);
Task task2 = DownloadAsync(url2);
Task task3 = DownloadAsync(url3);

// รอทั้งหมดเสร็จ
await Task.WhenAll(task1, task2, task3);

// หรือรอตัวไหนตัวนึงเสร็จ
Task completed = await Task.WhenAny(task1, task2, task3);

Best Practices

  1. Async all the way - ใช้ async ตลอด call stack
  2. Avoid async void - ยกเว้น event handlers เท่านั้น
  3. ConfigureAwait(false) - ใน Library code เพื่อไม่ capture context
  4. CancellationToken - รองรับการยกเลิก
  5. Don't block - อย่าใช้ .Result หรือ .Wait() (deadlock risk)

Anti-patterns ที่หลีกเลี่ยง

// ❌ ไม่ดี - Blocking call
var result = DoWorkAsync().Result;

// ❌ ไม่ดี - Sync over async
var result = Task.Run(() => DoWorkAsync()).Result;

// ✅ ดี - ใช้ await
var result = await DoWorkAsync();

สรุป

  • async ประกาศเมธอด asynchronous
  • await รอ Task เสร็จโดยไม่ block thread
  • Return type: Task, Task<T>, ValueTask<T>, void (event handler)
  • เพิ่ม Responsiveness และ Scalability
  • หลีกเลี่ยง .Result/.Wait() ใช้ await แทน