ทำไมต้องใช้ 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
- Async all the way - ใช้ async ตลอด call stack
- Avoid async void - ยกเว้น event handlers เท่านั้น
- ConfigureAwait(false) - ใน Library code เพื่อไม่ capture context
- CancellationToken - รองรับการยกเลิก
- 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ประกาศเมธอด asynchronousawaitรอ Task เสร็จโดยไม่ block thread- Return type: Task, Task<T>, ValueTask<T>, void (event handler)
- เพิ่ม Responsiveness และ Scalability
- หลีกเลี่ยง .Result/.Wait() ใช้ await แทน