Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Multithreading membutuhkan pemrograman yang cermat. Untuk sebagian besar tugas, Anda dapat mengurangi kompleksitas dengan mengantre permintaan untuk eksekusi berdasarkan utas kumpulan utas. Topik ini membahas situasi yang lebih sulit, seperti mengoordinasikan pekerjaan berbagai utas, atau menangani utas yang mengalami pemblokiran.
Nota
Dimulai dengan .NET Framework 4, Library Tugas Paralel dan PLINQ menyediakan API-API yang mengurangi kompleksitas dan risiko dalam pemrograman multi-utas. Untuk informasi selengkapnya, lihat Pemrograman Paralel di .NET.
Kebuntuan dan kondisi balapan
Multithreading memecahkan masalah dengan throughput dan responsivitas, tetapi dengan demikian hal itu memperkenalkan masalah baru: kebuntuan dan kondisi balapan.
Kebuntuan
Kebuntuan terjadi ketika dua utas mencoba mengunci sumber daya yang sudah dikunci oleh utas lainnya. Tidak ada utas yang dapat membuat kemajuan lebih lanjut.
Banyak metode kelas utas terkelola memberikan batas waktu untuk membantu Anda mendeteksi kebuntuan. Misalnya, kode berikut mencoba memperoleh kunci pada objek bernama lockObject. Jika kunci tidak diperoleh dalam 300 milidetik, Monitor.TryEnter mengembalikan false.
If Monitor.TryEnter(lockObject, 300) Then
Try
' Place code protected by the Monitor here.
Finally
Monitor.Exit(lockObject)
End Try
Else
' Code to execute if the attempt times out.
End If
if (Monitor.TryEnter(lockObject, 300)) {
try {
// Place code protected by the Monitor here.
}
finally {
Monitor.Exit(lockObject);
}
}
else {
// Code to execute if the attempt times out.
}
Kondisi balapan
Kondisi balapan adalah bug yang terjadi ketika hasil program tergantung pada utas mana dari dua atau lebih yang mencapai blok kode tertentu terlebih dahulu. Menjalankan program berkali-kali menghasilkan hasil yang berbeda, dan hasil dari eksekusi yang diberikan tidak dapat diprediksi.
Contoh sederhana dari kondisi balapan adalah menaikkan bidang. Misalkan kelas memiliki bidang statis privat (Dibagikan di Visual Basic) yang ditambahkan setiap kali instans kelas dibuat, menggunakan kode seperti objCt++; (C#) atau objCt += 1 (Visual Basic). Operasi ini mengharuskan memuat nilai dari objCt ke dalam register, meningkatkan nilai, dan menyimpannya di objCt.
Dalam aplikasi multithread, sebuah utas yang telah diload dan menaikkan nilai bisa didahului oleh utas lain yang melakukan ketiga-tiga langkah tersebut; ketika utas pertama melanjutkan eksekusi dan menyimpan nilainya, utas tersebut menimpa objCt tanpa memperhitungkan bahwa nilai tersebut telah berubah selama waktu ini.
Kondisi persaingan khusus ini mudah dihindari dengan menggunakan metode kelas Interlocked, seperti Interlocked.Increment. Untuk membaca tentang teknik lain untuk menyinkronkan data di antara beberapa utas, lihat Menyinkronkan Data untuk Multithreading.
Kondisi race juga dapat terjadi ketika Anda menyinkronkan aktivitas beberapa thread. Setiap kali Anda menulis baris kode, Anda harus mempertimbangkan apa yang mungkin terjadi jika sebuah utas dipreempt sebelum mengeksekusi baris (atau sebelum salah satu instruksi mesin individual yang menyusun baris), dan utas lain mengambil alih eksekusi.
Anggota statis dan konstruktor statis
Kelas tidak diinisialisasi sampai konstruktor kelasnya (static konstruktor di C#, Shared Sub New di Visual Basic) telah selesai berjalan. Untuk mencegah eksekusi kode pada tipe yang tidak diinisialisasi, common language runtime (CLR) memblokir semua panggilan dari utas lain ke static anggota kelas (Shared anggota di Visual Basic) hingga konstruktor kelas selesai berjalan.
Misalnya, jika konstruktor kelas memulai sebuah utas baru, dan prosedur utas tersebut memanggil anggota dari kelas tersebut, maka utas baru akan terblokir sampai konstruktor kelas selesai.
Ini berlaku untuk setiap tipe yang dapat memiliki static konstruktor.
Jumlah prosesor
Apakah ada beberapa prosesor atau hanya satu prosesor yang tersedia pada sistem yang dapat memengaruhi arsitektur multithreaded. Untuk informasi selengkapnya, lihat Jumlah Prosesor.
Environment.ProcessorCount Gunakan properti untuk menentukan jumlah prosesor yang tersedia saat runtime.
Rekomendasi umum
Pertimbangkan panduan berikut saat menggunakan beberapa thread:
Jangan gunakan Thread.Abort untuk mengakhiri utas lain. Memanggil
Abortdari utas lain mirip dengan melemparkan pengecualian pada utas tersebut, tanpa mengetahui pada tahap mana utas tersebut berada dalam pemrosesannya.Jangan gunakan Thread.Suspend dan Thread.Resume untuk menyinkronkan aktivitas beberapa utas. Harap gunakan Mutex, ManualResetEvent, AutoResetEvent, dan Monitor.
Jangan mengontrol eksekusi utas pekerja dari program utama Anda (misalnya menggunakan peristiwa). Sebagai gantinya, rancang program Anda sehingga utas pekerja bertanggung jawab untuk menunggu ketersediaan pekerjaan, menjalankan pekerjaan tersebut, serta memberi tahu bagian lain dari program Anda ketika selesai. Jika utas pekerja Anda tidak memblokir, pertimbangkan untuk menggunakan utas kumpulan utas. Monitor.PulseAll berguna dalam situasi di mana utas pekerja terhalang.
Jangan gunakan tipe data sebagai objek kunci. Artinya, hindari kode seperti
lock(typeof(X))di C# atauSyncLock(GetType(X))di Visual Basic, atau penggunaan Monitor.Enter dengan Type objek. Untuk tipe tertentu, hanya ada satu instance dari System.Type per domain aplikasi. Jika tipe yang Anda kunci bersifat publik, kode lain selain milik Anda sendiri dapat menguncinya, yang dapat menyebabkan kebuntuan. Untuk masalah tambahan, lihat Praktik Terbaik Keandalan.Berhati-hatilah saat mengunci instans, misalnya
lock(this)di C# atauSyncLock(Me)di Visual Basic. Jika kode lain dalam aplikasi Anda, di luar tipe, mengunci objek, dapat terjadi kebuntuan.Pastikan bahwa benang yang masuk dalam monitor selalu keluar dari monitor tersebut, bahkan jika terjadi pengecualian saat benang berada di monitor. Pernyataan lock C# dan pernyataan Visual Basic SyncLock memberikan perilaku ini secara otomatis, menggunakan blok finally untuk memastikan bahwa Monitor.Exit dipanggil. Jika Anda tidak dapat memastikan bahwa Exit akan dipanggil, pertimbangkan untuk mengubah desain Anda untuk menggunakan Mutex. Mutex secara otomatis dirilis ketika utas yang saat ini memilikinya berakhir.
Pastikan untuk menggunakan beberapa utas untuk tugas yang memerlukan sumber daya yang berbeda, dan hindari mengalokasikan beberapa utas ke satu sumber daya. Misalnya, tugas apa pun yang melibatkan I/O mendapat manfaat dari memiliki utasnya sendiri, karena utas tersebut akan terblokir selama operasi I/O sehingga memungkinkan utas lain untuk berjalan. Input pengguna adalah sumber daya lain yang mendapat manfaat dari jalur pemrosesan yang terpisah. Pada komputer dengan prosesor tunggal, tugas yang melibatkan komputasi intensif dapat berjalan bersamaan dengan input pengguna dan dengan tugas yang melibatkan I/O, tetapi beberapa tugas komputasi intensif saling berkompetisi.
Pertimbangkan untuk menggunakan metode Interlocked kelas untuk perubahan status sederhana, alih-alih menggunakan
lockpernyataan (SyncLockdi Visual Basic). Pernyataanlockadalah sebuah alat tujuan umum yang baik, tetapi kelas Interlocked memberikan performa yang lebih baik untuk pembaruan yang harus serba atom. Secara internal, ini menjalankan awalan kunci tunggal jika tidak ada ketidakcocokan. Dalam tinjauan kode, perhatikan kode seperti yang ditampilkan dalam contoh berikut. Dalam contoh pertama, variabel status dinaikkan:SyncLock lockObject myField += 1 End SyncLocklock(lockObject) { myField++; }Anda dapat meningkatkan performa dengan menggunakan Increment metode alih-alih
lockpernyataan, sebagai berikut:System.Threading.Interlocked.Increment(myField)System.Threading.Interlocked.Increment(myField);Nota
Gunakan metode Add untuk peningkatan atomik yang lebih besar dari 1.
Dalam contoh kedua, variabel jenis referensi diperbarui hanya jika merupakan referensi null (
Nothingdi Visual Basic).If x Is Nothing Then SyncLock lockObject If x Is Nothing Then x = y End If End SyncLock End Ifif (x == null) { lock (lockObject) { x ??= y; } }Performa dapat ditingkatkan dengan menggunakan metode CompareExchange sebagai gantinya, sebagai berikut:
System.Threading.Interlocked.CompareExchange(x, y, Nothing)System.Threading.Interlocked.CompareExchange(ref x, y, null);Nota
Kelebihan CompareExchange<T>(T, T, T) metode menyediakan alternatif jenis yang aman untuk jenis referensi.
Rekomendasi untuk pustaka kelas
Pertimbangkan panduan berikut saat mendesain pustaka kelas untuk multithreading:
Hindari kebutuhan akan sinkronisasi, jika memungkinkan. Ini terutama berlaku untuk kode yang banyak digunakan. Misalnya, algoritma mungkin disesuaikan untuk mentolerir kondisi balapan daripada menghilangkannya. Sinkronisasi yang tidak perlu mengurangi performa dan menciptakan kemungkinan kebuntuan dan kondisi balapan.
Buat utas data statis (
Shareddi Visual Basic) aman secara default.Jangan membuat utas data instans aman secara default. Menambahkan kunci untuk membuat kode aman utas mengurangi performa, meningkatkan ketidakcocokan kunci, dan menciptakan kemungkinan kebuntuan terjadi. Dalam model aplikasi umum, hanya satu utas pada satu waktu yang menjalankan kode pengguna, yang meminimalkan kebutuhan akan keamanan utas. Untuk alasan ini, pustaka kelas .NET tidak aman terhadap penggunaan oleh banyak thread secara default.
Hindari menyediakan metode statis yang mengubah status statis. Dalam skenario server umum, status statis dibagikan di seluruh permintaan, yang berarti beberapa utas dapat menjalankan kode tersebut secara bersamaan. Ini membuka kemungkinan masalah bug terkait penggunaan utas. Pertimbangkan untuk menggunakan pola desain yang merangkum data ke dalam instans yang tidak dibagikan di seluruh permintaan. Selain itu, jika data statis disinkronkan, panggilan antara metode statis yang mengubah status dapat mengakibatkan kebuntuan atau sinkronisasi redundan, berdampak buruk pada performa.