Dictionary<TKey, TValue> dan Queue<T> tidak dirancang untuk disentuh beberapa thread sekaligus; isi bisa ganda hitung, hilang, atau menghasilkan keadaan yang tidak valid. Namespace System.Collections.Concurrent menyediakan koleksi yang menangani situasi ini: setiap operasinya aman dipanggil dari banyak thread tanpa penguncian manual. Bab ini hanya memperkenalkan API dua koleksi yang paling sering dipakai; detail pemrograman thread dibahas pada section tersendiri.
Dua Koleksi yang Paling Sering Dipakai #
ConcurrentDictionary<TKey, TValue>: kamus aman thread dengan method kombinasi yang menutup celah antara cek dan aksi, sepertiGetOrAdddanAddOrUpdate.ConcurrentQueue<T>: antrean FIFO aman thread; operasi keluar-masuk memakai polaTry, misalnyaTryDequeue, karena isi antrean bisa berubah antar thread kapan saja.
Method kombinasi adalah kunci desainnya. Pada Dictionary<TKey, TValue> biasa, urutan if (!dict.ContainsKey(k)) lalu dict[k] = v terdiri atas dua langkah terpisah; thread lain bisa menyisipkan aksi di antaranya. GetOrAdd menjalankan cek dan aksi sebagai satu operasi atomik.
Contoh: API tanpa Thread #
Contoh berikut memakai kedua koleksi secara berurutan dalam satu thread, agar perilaku tiap method terlihat murni:
using System.Collections.Concurrent;
ConcurrentDictionary<string, int> stokGudang = new ConcurrentDictionary<string, int>();
int stokAwal = stokGudang.GetOrAdd("Kertas A4", 500);
int stokBaru = stokGudang.AddOrUpdate(
"Kertas A4",
0,
(namaBarang, nilaiLama) => nilaiLama - 40);
Console.WriteLine($"Stok awal: {stokAwal}");
Console.WriteLine($"Stok setelah pengurangan: {stokBaru}");
ConcurrentQueue<string> antreanCetak = new ConcurrentQueue<string>();
antreanCetak.Enqueue("Invoice-1042.pdf");
antreanCetak.Enqueue("Laporan-Omzet.pdf");
if (antreanCetak.TryDequeue(out string? dokumen))
{
Console.WriteLine($"Mencetak: {dokumen}");
}
Console.WriteLine($"Sisa antrean: {antreanCetak.Count}");Output yang Dihasilkan:
Stok awal: 500
Stok setelah pengurangan: 460
Mencetak: Invoice-1042.pdf
Sisa antrean: 1Mari kita bedah baris demi baris:
stokGudang.GetOrAdd("Kertas A4", 500): kunci belum ada, sehingga nilai 500 ditambahkan dan dikembalikan; bila kunci sudah ada, method hanya membaca nilai lama tanpa menimpanya.stokGudang.AddOrUpdate(...): satu operasi atomik yang mengurangi stok 40; lambda menerima nama kunci dan nilai lama, lalu mengembalikan nilai baru.antreanCetak.Enqueue(...): dua dokumen masuk dengan urutan FIFO, sama sepertiQueue<T>biasa.antreanCetak.TryDequeue(out string? dokumen): polaTrymengembalikanbool; pemanggil memaksa program menangani kasus antrean kosong sebagai bagian normal dari API.antreanCetak.Count: angka ini adalah perkiraan saat itu juga; pada program multithread, nilai bisa berubah tepat setelah dibaca.
Setelah semua keluarga koleksi terpetakan, dua bab penutup membahas cara menilai biayanya dan cara merancang koleksi di dalam API kita sendiri.