Giới thiệu
Trong lập trình C#, async và await là hai từ khóa quan trọng giúp tạo ra các phương thức bất đồng bộ. Điều này cho phép bạn thực hiện các tác vụ mà không làm nghẽn giao diện người dùng (UI) hoặc làm chậm ứng dụng của bạn. Bài viết này sẽ đi sâu vào cách sử dụng async và await, cùng với các thực tiễn tốt nhất và những cạm bẫy thường gặp.
Nội dung
- Cách thức hoạt động của async và await
- Tìm hiểu về Task và Task
- So sánh Task.Delay và Thread.Sleep
- Sử dụng Task.Run để chạy tác vụ nặng
- Hiểu rõ Task.WhenAll và Task.WhenAny
- Cách sử dụng ConfigureAwait
- Tìm hiểu về CancellationToken
- Sử dụng IAsyncEnumerable
- Danh sách các API async phổ biến
- Kết luận
Cách thức hoạt động của async và await {#cach-thuc-hoat-dong}
async được sử dụng để khai báo một phương thức là bất đồng bộ. Khi bạn khai báo một phương thức với từ khóa async, điều này cho phép bạn sử dụng từ khóa await bên trong phương thức đó.
csharp
public async Task RunAsync()
{
Console.WriteLine("Bắt đầu");
await Task.Delay(2000); // 2 giây chờ, nhưng các mã khác vẫn có thể thực thi
Console.WriteLine("Kết thúc");
}
Khi bạn gọi RunAsync, phương thức này sẽ bắt đầu thực thi, nhưng sau khi gặp await, nó sẽ cho phép các tác vụ khác chạy trong khi chờ đợi.
Tìm hiểu về Task và Task {#task}
Tasklà một loại đối tượng đại diện cho một tác vụ không trả về kết quả.Task<TResult>là một loại đối tượng đại diện cho một tác vụ trả về một giá trị.
csharp
public async Task<int> GetNumberAsync()
{
await Task.Delay(1000); // mô phỏng một tác vụ mất thời gian
return 42;
}
int result = await GetNumberAsync(); // result sẽ là 42
So sánh Task.Delay và Thread.Sleep {#so-sanh-taskdelay-threadsleep}
Task.Delaycho phép bạn chờ một khoảng thời gian mà không làm nghẽn luồng, trong khiThread.Sleepsẽ làm nghẽn luồng.
csharp
// Điều này sẽ làm UI bị đơ
Thread.Sleep(3000);
// Điều này sẽ không làm UI bị đơ
await Task.Delay(3000);
Sử dụng Task.Run để chạy tác vụ nặng {#taskrun}
Task.Run cho phép bạn thực hiện các tác vụ nặng trong một luồng riêng biệt.
csharp
await Task.Run(() =>
{
// Tính toán nặng
Thread.Sleep(2000);
Console.WriteLine("Tính toán đã hoàn tất");
});
Hiểu rõ Task.WhenAll và Task.WhenAny {#whenall-whenany}
Task.WhenAllcho phép bạn chờ tất cả các tác vụ hoàn thành.Task.WhenAnycho phép bạn chờ tác vụ đầu tiên hoàn thành.
csharp
var t1 = Task.Delay(2000);
var t2 = Task.Delay(3000);
await Task.WhenAll(t1, t2); // chờ cả hai tác vụ hoàn thành
await Task.WhenAny(t1, t2); // chờ tác vụ đầu tiên hoàn thành
Cách sử dụng ConfigureAwait {#configureawait}
Khi làm việc với các ứng dụng server (ASP.NET Core) hoặc ứng dụng UI, việc chuyển đổi ngữ cảnh là rất quan trọng.
csharp
await SomeTask().ConfigureAwait(false);
Điều này cho phép bạn không quay lại ngữ cảnh UI, và thường được sử dụng trong mã server.
Tìm hiểu về CancellationToken {#cancellationtoken}
CancellationToken được sử dụng để hủy bỏ một tác vụ bất đồng bộ.
csharp
public async Task RunAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
token.ThrowIfCancellationRequested();
await Task.Delay(500);
Console.WriteLine(i);
}
}
Sử dụng IAsyncEnumerable {#iasyncenumerable}
IAsyncEnumerable cho phép bạn lấy dữ liệu một cách tuần tự từ một nguồn dữ liệu lớn.
csharp
public async IAsyncEnumerable<int> GetNumbersAsync()
{
for (int i = 1; i <= 5; i++)
{
await Task.Delay(500);
yield return i;
}
}
await foreach (var num in GetNumbersAsync())
{
Console.WriteLine(num);
}
Danh sách các API async phổ biến {#common-async-apis}
Dưới đây là một số API async thường được sử dụng trong C# và .NET:
Task.Delay– chờ một khoảng thời gian.HttpClient.GetAsync– lấy dữ liệu từ internet.Stream.ReadAsync / WriteAsync– làm việc với file/stream.SignalR.SendAsync– gửi tin nhắn thời gian thực.DbContext.SaveChangesAsync– ghi vào cơ sở dữ liệu.
Kết luận {#ket-luan}
- Sử dụng
Thread.Sleepsẽ làm nghẽn luồng và làm UI không phản hồi. - Sử dụng
Task.Delaygiúp chờ mà không làm nghẽn luồng. Task.Rungiúp thực hiện các tác vụ nặng trong một luồng riêng.Task.WhenAllvàTask.WhenAnycho phép quản lý các tác vụ song song.CancellationTokengiúp hủy bỏ tác vụ khi cần thiết.IAsyncEnumerablecho phép lấy dữ liệu tuần tự từ nguồn dữ liệu lớn.
FAQs
1. Làm thế nào để hủy bỏ một tác vụ async?
Bạn có thể sử dụng CancellationToken để hủy bỏ tác vụ khi cần thiết.
2. Sự khác biệt giữa Task và Task
Task không trả về giá trị, trong khi Task<TResult> trả về một giá trị cụ thể.
3. Có cách nào để chạy tác vụ nặng mà không làm UI bị đơ không?
Sử dụng Task.Run để chạy tác vụ nặng trong một luồng riêng biệt mà không làm UI bị đơ.
Hy vọng bài viết này đã giúp bạn hiểu rõ hơn về cách sử dụng async và await trong lập trình C#. Hãy thử áp dụng những kiến thức này vào dự án của bạn nhé!