Bất kỳ lúc nào bạn cần thực hiện tác vụ trên máy chủ từ xa, chương trình của bạn sẽ tạo yêu cầu, gửi yêu cầu, sau đó chờ phản hồi. Tôi sẽ sử dụng SaveChanges()
và SaveChangesAsync()
làm ví dụ nhưng cũng áp dụng cho Find()
và FindAsync()
.
Giả sử bạn có danh sách myList
trong số 100+ mục bạn cần thêm vào cơ sở dữ liệu của mình. Để chèn đó, chức năng của bạn sẽ giống như vậy:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Trước tiên, bạn tạo và thể hiện của MyEDM
, thêm danh sách myList
vào bảng MyTable
, sau đó gọi SaveChanges()
để kéo dài sự thay đổi đối với cơ sở dữ liệu. Nó hoạt động như thế nào bạn muốn, các hồ sơ có được cam kết, nhưng chương trình của bạn không thể làm bất cứ điều gì khác cho đến khi cam kết kết thúc. Điều này có thể mất nhiều thời gian tùy thuộc vào những gì bạn đang cam kết. Nếu bạn đang thực hiện thay đổi đối với hồ sơ, thực thể phải cam kết một lần tại một thời điểm (tôi đã từng tiết kiệm mất 2 phút để cập nhật)!
Để giải quyết vấn đề này, bạn có thể thực hiện một trong hai điều. Đầu tiên là bạn có thể bắt đầu một chuỗi mới để xử lý chèn. Trong khi điều này sẽ giải phóng các thread gọi để tiếp tục thực hiện, bạn tạo ra một chủ đề mới chỉ là sẽ ngồi đó và chờ đợi. Không cần thiết cho chi phí đó, và đây là những gì mà mẫu hình async await
giải quyết.
Đối với các giai đoạn I/O, await
nhanh chóng trở thành người bạn tốt nhất của bạn.Lấy phần mã từ phía trên, chúng tôi có thể sửa đổi nó thành:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
Đây là một thay đổi rất nhỏ, nhưng có tác động sâu sắc đến hiệu quả và hiệu suất của mã của bạn. Vậy điều gì sẽ xảy ra? Sự khởi đầu của mã là như nhau, bạn tạo một thể hiện của MyEDM
và thêm myList
của bạn vào MyTable
. Nhưng khi bạn gọi await context.SaveChangesAsync()
, việc thực thi mã sẽ trở về chức năng gọi điện! Vì vậy, trong khi bạn đang chờ đợi tất cả các hồ sơ đó cam kết, mã của bạn có thể tiếp tục thực thi. Nói chức năng có chứa đoạn mã trên có chữ ký của public async Task SaveRecords(List<MyTable> saveList)
, chức năng gọi điện thoại có thể nhìn như thế này:
public async Task MyCallingFunction()
{
Console.WriteLine("Function Starting");
Task saveTask = SaveRecords(GenerateNewRecords());
for(int i = 0; i < 1000; i++){
Console.WriteLine("Continuing to execute!");
}
await saveTask;
Console.Log("Function Complete");
}
Tại sao bạn sẽ có một chức năng như thế này, tôi không biết, nhưng những gì nó ra cho thấy cách async await
hoạt động. Đầu tiên cho phép đi qua những gì sẽ xảy ra.
Thực hiện nhập MyCallingFunction
, Function Starting
sau đó Save Starting
được ghi vào bảng điều khiển, sau đó chức năng SaveChangesAsync()
được gọi. Tại thời điểm này, thực hiện trả về MyCallingFunction
và nhập vòng lặp for viết 'Tiếp tục thực thi' lên tới 1000 lần. Khi SaveChangesAsync()
phần lan, thực thi trả về hàm SaveRecords
, viết Save Complete
vào bảng điều khiển. Khi mọi thứ trong SaveRecords
hoàn tất, việc thực thi sẽ tiếp tục trong MyCallingFunction
ngay khi nó hoàn thành khi SaveChangesAsync()
hoàn tất. Bối rối? Dưới đây là một ví dụ đầu ra:
Function Starting
Save Starting
Continuing to execute!
Continuing to execute!
Continuing to execute!
Continuing to execute!
Continuing to execute!
....
Continuing to execute!
Save Complete!
Continuing to execute!
Continuing to execute!
Continuing to execute!
....
Continuing to execute!
Function Complete!
Hoặc có thể:
Function Starting
Save Starting
Continuing to execute!
Continuing to execute!
Save Complete!
Continuing to execute!
Continuing to execute!
Continuing to execute!
....
Continuing to execute!
Function Complete!
Đó là vẻ đẹp của async await
, mã của bạn có thể tiếp tục chạy trong khi bạn đang chờ đợi một cái gì đó để kết thúc. Trên thực tế, bạn sẽ có một chức năng hơn như thế này như chức năng của bạn gọi:
public async Task MyCallingFunction()
{
List<Task> myTasks = new List<Task>();
myTasks.Add(SaveRecords(GenerateNewRecords()));
myTasks.Add(SaveRecords2(GenerateNewRecords2()));
myTasks.Add(SaveRecords3(GenerateNewRecords3()));
myTasks.Add(SaveRecords4(GenerateNewRecords4()));
await Task.WhenAll(myTasks.ToArray());
}
Ở đây, bạn có bốn chức năng ghi lại tiết kiệm khác nhau sẽ cùng lúc. MyCallingFunction
sẽ hoàn thành nhanh hơn rất nhiều bằng cách sử dụng async await
sau đó nếu các chức năng riêng lẻ SaveRecords
được gọi theo chuỗi.
Điều mà tôi chưa chạm vào là từ khóa await
. Điều này có nghĩa là dừng chức năng hiện tại để thực thi cho đến khi bất cứ điều gì Task
bạn đang đợi hoàn tất. Vì vậy, trong trường hợp của MyCallingFunction
ban đầu, dòng Function Complete
sẽ không được ghi vào bảng điều khiển cho đến khi chức năng SaveRecords
kết thúc.
Ngắn câu chuyện ngắn, nếu bạn có tùy chọn sử dụng async await
, bạn nên làm như vậy sẽ làm tăng hiệu suất của ứng dụng của bạn.
không đồng bộ nhiều, * nhiều hơn * ngăn chặn chuỗi giao diện người dùng của khách hàng chặn trong ứng dụng khách. Tôi chắc rằng có một câu trả lời của chuyên gia sẽ sớm xuất hiện. – jdphenix