Operasi berkas membutuhkan waktu karena datanya melintasi perangkat penyimpanan, dan metode asinkron membuat thread yang menunggu operasi tersebut tidak terjebak diam. Topik ini membahas metode I/O berkas asinkron yang dipakai dengan await, pembacaan StreamReader secara asinkron, serta pembatalan operasi menggunakan CancellationToken yang dibatalkan secara eksplisit. Seluruh contoh dijalankan sebagai program utuh dan keluarannya ditampilkan apa adanya.
Metode berkas asinkron#
Kelas File menyediakan pasangan asinkron untuk hampir semua metode sinkronnya, misalnya WriteAllTextAsync, ReadAllTextAsync, dan WriteAllLinesAsync. Nama metode asinkron mengikuti pola yang sama dengan sinkronnya ditambah akhiran Async, sehingga korespondensinya mudah dikenali, dan argumen yang diterimanya pun sama dengan tambahan token pembatalan opsional di bagian akhir. Metode-metode tersebut mengembalikan Task, dan kata kunci await menunggu selesainya operasi sambil melepaskan thread pemanggil untuk pekerjaan lain. Program C# modern memungkinkan await ditulis langsung pada pernyataan tingkat atas (top-level statements), sehingga program berkas tunggal tidak memerlukan deklarasi metode async Main. Contoh berikut menulis lima bacaan sensor secara asinkron, membacanya kembali, lalu menambahkan satu baris ringkasan dan membacanya lewat StreamReader.
// Program: I/O berkas asinkron dengan await
string jalur = "pengukuran.txt";
string[] bacaan = Enumerable.Range(1, 5).Select(i => $"bacaan {i}").ToArray();
await File.WriteAllLinesAsync(jalur, bacaan);
string isi = await File.ReadAllTextAsync(jalur);
Console.WriteLine(isi);
using (StreamWriter penulis = new(jalur, append: true))
{
await penulis.WriteLineAsync("ringkasan: 5 bacaan");
}
using (StreamReader pembaca = new(jalur))
{
string? baris;
while ((baris = await pembaca.ReadLineAsync()) is not null)
{
Console.WriteLine($"dari stream: {baris}");
}
}
File.Delete(jalur);
Console.WriteLine($"Berkas ada setelah dihapus: {File.Exists(jalur)}");bacaan 1
bacaan 2
bacaan 3
bacaan 4
bacaan 5
dari stream: bacaan 1
dari stream: bacaan 2
dari stream: bacaan 3
dari stream: bacaan 4
dari stream: bacaan 5
dari stream: ringkasan: 5 bacaan
Berkas ada setelah dihapus: FalseUntuk menjalankan program tersebut dari folder topik, gunakan perintah berikut.
# aktifkan .NET yang terpasang di sandbox
export DOTNET_ROOT=/home/kal/.dotnet PATH=/home/kal/.dotnet:$PATH DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_NOLOGO=1
# jalankan program contoh dari folder topik
dotnet run --file Program.csbacaan 1
bacaan 2
bacaan 3
bacaan 4
bacaan 5
dari stream: bacaan 1
dari stream: bacaan 2
dari stream: bacaan 3
dari stream: bacaan 4
dari stream: bacaan 5
dari stream: ringkasan: 5 bacaan
Berkas ada setelah dihapus: FalseSatu baris kosong muncul setelah baris kelima karena WriteAllLinesAsync menyimpan penanda baris di akhir setiap elemen, sehingga ReadAllTextAsync mengembalikan teks yang diakhiri penanda baris dan WriteLine menambahkan penanda miliknya sendiri. Metode WriteLineAsync dan ReadLineAsync pada StreamWriter dan StreamReader menjalankan peran yang sama dengan versi sinkronnya, hanya saja keduanya di-await. Penanda is not null pada perulangan memastikan bacaan null di akhir stream dipisahkan dari teks baris yang sah.
Penanganan exception pada kode asinkron tetap memakai try-catch biasa, dan blok catch diletakkan pada metode yang memanggil await. Exception yang dilempar operasi asinkron disimpan di dalam Task-nya, kemudian dilempar ulang pada baris await, sehingga lokasi penangkapannya sama dengan lokasi pemanggilannya. Pola penanganan error berkas seperti menangkap FileNotFoundException karenanya berlaku tanpa perubahan pada versi asinkronnya.
Pembatalan dengan CancellationToken#
Operasi berkas yang berlangsung lama sebaiknya dapat dibatalkan, dan mekanismenya adalah CancellationToken, yaitu penanda pembatalan yang dikirim ke metode asinkron. Penanda tersebut berasal dari CancellationTokenSource, yang menyimpan keadaan pembatalan dan membagikan property Token-nya kepada semua operasi yang terlibat. Pemanggilan metode Cancel pada sumbernya menandai token sebagai dibatalkan, dan operasi yang menerima token akan melempar OperationCanceledException pada titik pemeriksaan berikutnya. Pembatalan bersifat kooperatif karena operasinya sendiri yang memeriksa token, bukan sumbernya yang menghentikan operasi secara paksa. Contoh berikut menjalankan dua pembacaan dengan token yang sama, yaitu satu sebelum dan satu sesudah token dibatalkan.
// Contoh: CancellationToken yang dibatalkan secara eksplisit
await File.WriteAllTextAsync("pengukuran.txt", "bacaan 1\nbacaan 2\nbacaan 3");
using CancellationTokenSource sumberToken = new();
string isi = await File.ReadAllTextAsync("pengukuran.txt", sumberToken.Token);
Console.WriteLine($"Pembacaan pertama berhasil: {isi.Length} karakter");
sumberToken.Cancel();
try
{
await File.ReadAllTextAsync("pengukuran.txt", sumberToken.Token);
Console.WriteLine("Pembacaan kedua berhasil");
}
catch (OperationCanceledException)
{
Console.WriteLine("Pembacaan kedua dibatalkan karena token sudah dibatalkan");
}
File.Delete("pengukuran.txt");Pembacaan pertama berhasil: 26 karakter
Pembacaan kedua dibatalkan karena token sudah dibatalkanPembacaan pertama selesai karena token-nya masih aktif, sedangkan pembacaan kedua ditolak segera setelah Cancel dipanggil. Pada program nyata, pemanggilan Cancel biasanya dilakukan dari tempat lain, misalnya saat pengguna menekan tombol berhenti atau ketika permintaan jaringan diputus, sedangkan kode di atas memanggilnya di titik tetap agar keluarannya deterministik. Token yang dibatalkan sebelum operasi dimulai ditolak tanpa menyentuh perangkat penyimpanan sama sekali, sedangkan pembatalan di tengah operasi baru terdeteksi pada titik pemeriksaan berikutnya. Metode asinkron pada Stream juga menerima token, misalnya ReadToEndAsync dan CopyToAsync, sehingga satu token dapat dibagikan ke seluruh rantai operasi yang dibatalkan sekaligus. Apabila pembatalan datang dari beberapa sumber, CreateLinkedTokenSource menyatukan beberapa token menjadi satu, sedangkan CancellationToken.None dipakai ketika pembatalan memang tidak diperlukan.
Kapan async layak digunakan#
Metode asinkron tidak membuat berkas terbaca lebih cepat, melainkan membuat thread yang menunggunya bebas melakukan pekerjaan lain selama operasi berlangsung. Pada program baris perintah yang menunggu satu berkas lalu selesai, versi sinkron sudah cukup karena tidak ada pekerjaan lain yang tertunda. Sebaliknya, pada aplikasi web yang menangani banyak permintaan atau aplikasi berantarmuka grafis yang menjaga responsivitas layar, versi asinkron mencegah satu operasi berkas menahan seluruh thread penangan permintaan atau thread antarmuka. Panduan praktisnya sederhana, yaitu menggunakan versi asinkron ketika kode berjalan di lingkungan yang punya pekerjaan lain selama menunggu.
Satu hal yang perlu dijaga adalah keadaan menunggu yang bergantung. Deretan await di dalam satu metode berjalan satu per satu, sehingga pembacaan lima berkas secara berurutan tetap memakan jumlah waktu kelima operasinya. Pembacaan yang saling tidak bergantung dapat dikumpulkan menjadi kumpulan Task dan ditunggu bersama melalui Task.WhenAll agar waktunya tumpang tindih. Pemilihan pola ini tetap diawali pengukuran, karena penambahan async pada program yang tidak menunggu apa pun hanya menambah kompleksitas tanpa manfaat.
Satu pengecualian kecil perlu dicatat, yaitu penulisan versi asinkron tetap disarankan pada pustaka (library) yang akan dipakai ulang, sekalipun pemanggilnya saat ini tidak menunggu apa pun. Alasannya, tanda tangan metode sinkron tidak bisa diubah menjadi asinkron tanpa merusak pemanggil yang sudah ada, sehingga keputusan menunggu lebih baik diambil sejak awal oleh pustakanya.
Ringkasan#
- Metode seperti
WriteAllTextAsync,ReadAllTextAsync, danWriteLineAsyncmenjalankan I/O berkas tanpa menahan thread pemanggilnya. awaitdapat ditulis pada pernyataan tingkat atas, sehingga program berkas tunggal tidak memerlukanasync Main.CancellationTokenSourcemembuat token yang dikirim ke metode asinkron, dan pemanggilanCancelmemicuOperationCanceledExceptionpada operasi yang memeriksa token.- Pembatalan bersifat kooperatif dan deterministik sepanjang pemanggilan
Cancelberada di titik yang tetap.
Referensi#
- Asynchronous file I/O — Microsoft Learn — Panduan I/O berkas asinkron pada .NET beserta contoh penggunaannya.
- CancellationTokenSource class — Microsoft Learn
— Referensi kelas sumber token pembatalan beserta metode
Canceldan propertyToken.