Kapan pun Anda perlu melakukan tindakan di server jarak jauh, program Anda membuat permintaan, mengirimkannya, lalu menunggu tanggapan. Saya akan menggunakan SaveChanges()
dan SaveChangesAsync()
sebagai contoh tetapi hal yang sama berlaku untuk Find()
dan FindAsync()
.
Katakanlah Anda memiliki daftar myList
100+ item yang perlu Anda tambahkan ke database Anda. Untuk memasukkannya, fungsi Anda akan terlihat seperti ini:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Pertama Anda membuat instance MyEDM
, menambahkan daftar myList
ke tabel MyTable
, lalu memanggil SaveChanges()
untuk mempertahankan perubahan ke database. Ini berfungsi seperti yang Anda inginkan, record dikomit, tetapi program Anda tidak dapat melakukan apa pun sampai komit selesai. Ini bisa memakan waktu lama tergantung pada apa yang Anda lakukan. Jika Anda melakukan perubahan pada record, entitas harus mengkomitnya satu per satu (saya pernah menyimpan membutuhkan waktu 2 menit untuk update)!
Untuk mengatasi masalah ini, Anda dapat melakukan salah satu dari dua hal. Yang pertama adalah Anda dapat memulai utas baru untuk menangani penyisipan. Meskipun ini akan membebaskan utas panggilan untuk terus mengeksekusi, Anda membuat utas baru yang hanya akan duduk di sana dan menunggu. Tidak perlu biaya tambahan itu, dan inilah yang async await
dipecahkan oleh polanya.
Untuk pengoperasian I / O, await
cepatlah menjadi sahabat Anda. Mengambil bagian kode dari atas, kita dapat memodifikasinya menjadi:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
Ini adalah perubahan yang sangat kecil, tetapi ada efek yang sangat besar pada efisiensi dan kinerja kode Anda. Lalu apa yang terjadi? Awal kodenya sama, Anda membuat instance MyEDM
dan menambahkan myList
ke MyTable
. Tetapi ketika Anda memanggil await context.SaveChangesAsync()
, eksekusi kode kembali ke fungsi panggilan! Jadi, saat Anda menunggu semua record tersebut dijalankan, kode Anda dapat terus dieksekusi. Katakanlah fungsi yang berisi kode di atas memiliki tanda tangan public async Task SaveRecords(List<MyTable> saveList)
, fungsi pemanggilan bisa terlihat seperti ini:
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");
}
Mengapa Anda memiliki fungsi seperti ini, saya tidak tahu, tetapi apa yang dihasilkannya menunjukkan cara async await
kerjanya. Pertama, mari kita bahas apa yang terjadi.
Eksekusi masuk MyCallingFunction
, Function Starting
lalu Save Starting
ditulis ke konsol, lalu fungsi SaveChangesAsync()
dipanggil. Pada titik ini, eksekusi kembali ke MyCallingFunction
dan memasuki penulisan loop for 'Continuing to Execute' hingga 1000 kali. Setelah SaveChangesAsync()
selesai, eksekusi kembali ke SaveRecords
fungsi, menulis Save Complete
ke konsol. Setelah semuanya SaveRecords
selesai, eksekusi akan dilanjutkan MyCallingFunction
jika sudah SaveChangesAsync()
selesai. Bingung? Berikut adalah contoh keluarannya:
Fungsi Mulai
Simpan Mulai
Terus mengeksekusi!
Terus mengeksekusi!
Terus mengeksekusi!
Terus mengeksekusi!
Terus mengeksekusi!
....
Terus mengeksekusi!
Simpan Selesai!
Terus mengeksekusi!
Terus mengeksekusi!
Terus mengeksekusi!
....
Terus mengeksekusi!
Fungsi Selesai!
Atau mungkin:
Fungsi Mulai
Simpan Mulai
Terus mengeksekusi!
Terus mengeksekusi!
Simpan Selesai!
Terus mengeksekusi!
Terus mengeksekusi!
Terus mengeksekusi!
....
Terus mengeksekusi!
Fungsi Selesai!
Itulah keindahannya async await
, kode Anda dapat terus berjalan saat Anda menunggu sesuatu untuk diselesaikan. Pada kenyataannya, Anda akan memiliki fungsi yang lebih seperti ini sebagai fungsi panggilan Anda:
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());
}
Di sini, Anda memiliki empat fungsi penyimpanan catatan yang berbeda yang berjalan pada saat bersamaan . MyCallingFunction
akan menyelesaikan penggunaan lebih cepat async await
daripada jika SaveRecords
fungsi individu dipanggil secara seri.
Satu hal yang belum saya singgung adalah await
kata kunci. Apa yang dilakukannya adalah menghentikan fungsi saat ini dari eksekusi hingga apa pun yang Task
Anda tunggu selesai. Jadi dalam kasus aslinya MyCallingFunction
, baris Function Complete
tidak akan ditulis ke konsol sampai SaveRecords
fungsinya selesai.
Singkat cerita, jika Anda memiliki opsi untuk menggunakan async await
, Anda harus melakukannya karena akan sangat meningkatkan kinerja aplikasi Anda.