Bất kỳ lúc nào bạn cần thực hiện một hành động trên máy chủ từ xa, chương trình của bạn sẽ tạo ra 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 điều tương tự cũng áp dụng cho Find()vàFindAsync() .
Giả sử bạn có một danh sách myListhơn 100 mục mà bạn cần thêm vào cơ sở dữ liệu của mình. Để chèn điều đó, hàm của bạn sẽ trông giống như sau:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Đầu tiên, bạn tạo một phiên bản MyEDM, thêm danh sách myListvào bảng MyTable, sau đó gọi SaveChanges()để duy trì các thay đổi đối với cơ sở dữ liệu. Nó hoạt động theo cách bạn muốn, các bản ghi đượ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. Quá trình 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 các thay đổi đối với các bản ghi, thực thể phải thực hiện những thay đổi đó cùng một lúc (tôi đã từng có một lần lưu mất 2 phút để cập nhật)!
Để giải quyết vấn đề này, bạn có thể làm một trong hai điều. Đầu tiên là bạn có thể khởi động một luồng mới để xử lý phần chèn. Trong khi điều này sẽ giải phóng chuỗi gọi để tiếp tục thực thi, bạn đã tạo một chuỗi mới chỉ cần ngồi đó và chờ đợi. Không cần chi phí cao đó, và đây là những gì async awaitmô hình giải quyết.
Đối với các đối tác I / O, hãy awaitnhanh chóng trở thành người bạn tốt nhất của bạn. Lấy phần mã từ trên xuống, chúng ta 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");
}
Đó là một thay đổi rất nhỏ, nhưng có những ảnh hưở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ì xảy ra? Đầu mã giống nhau, bạn tạo một phiên bản của MyEDMvà thêm myListvào MyTable. Nhưng khi bạn gọi await context.SaveChangesAsync(), việc thực thi mã trở lại hàm gọi! Vì vậy, trong khi bạn đang đợi tất cả các bản ghi đó cam kết, mã của bạn có thể tiếp tục thực thi. Giả sử hàm chứa đoạn mã trên có chữ ký của public async Task SaveRecords(List<MyTable> saveList), hàm gọi có thể giống như sau:
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 lại có một chức năng như thế này, tôi không biết, nhưng những gì nó xuất ra cho thấy cách async awaithoạt động. Đầu tiên chúng ta hãy xem xét những gì sẽ xảy ra.
Việc thực thi đi vào MyCallingFunction, Function Startingsau đó Save Startingđược ghi vào bảng điều khiển, sau đó hàm SaveChangesAsync()được gọi. Tại thời điểm này, việc thực thi quay trở lại MyCallingFunctionvà đi vào vòng lặp for viết 'Tiếp tục thực thi' lên đến 1000 lần. Khi SaveChangesAsync()kết thúc, việc thực thi trở lại SaveRecordshàm, ghi Save Completevào bảng điều khiển. Khi mọi thứ SaveRecordshoàn tất, quá trình thực thi sẽ tiếp tục MyCallingFunctionđúng như khi SaveChangesAsync()hoàn thành. Bối rối? Đây là một ví dụ đầu ra:
Chức năng Bắt đầu
Lưu bắt đầu
Tiếp tục thực hiện!
Tiếp tục thực hiện!
Tiếp tục thực hiện!
Tiếp tục thực hiện!
Tiếp tục thực hiện!
....
Tiếp tục thực hiện!
Lưu Hoàn thành!
Tiếp tục thực hiện!
Tiếp tục thực hiện!
Tiếp tục thực hiện!
....
Tiếp tục thực hiện!
Chức năng hoàn thành!
Hoặc có thể:
Chức năng Bắt đầu
Lưu bắt đầu
Tiếp tục thực hiện!
Tiếp tục thực hiện!
Lưu Hoàn thành!
Tiếp tục thực hiện!
Tiếp tục thực hiện!
Tiếp tục thực hiện!
....
Tiếp tục thực hiện!
Chức năng hoàn thành!
Đó 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 đợi một thứ gì đó kết thúc. Trong thực tế, bạn sẽ có một chức năng giống như chức năng gọi của bạn:
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 lưu bản ghi khác nhau hoạt động cùng một lúc . MyCallingFunctionsẽ hoàn thành nhanh hơn rất nhiều async awaitso với khi các SaveRecordschức năng riêng lẻ được gọi theo chuỗi.
Một điều mà tôi chưa chạm vào là awaittừ khóa. Điều này làm là ngăn chức năng hiện tại thực thi cho đến khi bất cứ điều gì Taskbạn đang chờ hoàn thành. Vì vậy, trong trường hợp của bản gốc MyCallingFunction, dòng Function Completesẽ không được ghi vào bảng điều khiển cho đến khi SaveRecordschức năng kết thúc.
Tóm lại, nếu bạn có một tùy chọn để sử dụng async await, bạn nên sử dụng vì nó sẽ làm tăng đáng kể hiệu suất của ứng dụng của bạn.