Apakah ini berarti bahwa dua utas tidak dapat mengubah data yang mendasarinya secara bersamaan? Atau apakah itu berarti bahwa segmen kode yang diberikan akan berjalan dengan hasil yang dapat diprediksi ketika beberapa utas mengeksekusi segmen kode itu?
multithreading
language-agnostic
programming-languages
concurrency
Varun Mahajan
sumber
sumber
Jawaban:
Dari Wikipedia:
Baca lebih banyak:
http://en.wikipedia.org/wiki/Thread_safety
dalam bahasa Jerman: http://de.wikipedia.org/wiki/Threadsicherheit
dalam bahasa Prancis: http://fr.wikipedia.org/wiki/Threadsafe (sangat singkat)
sumber
Kode thread-aman adalah kode yang akan berfungsi bahkan jika banyak Thread mengeksekusi secara bersamaan.
http://mindprod.com/jgloss/threadsafe.html
sumber
Pertanyaan yang lebih informatif adalah apa yang membuat kode bukan utas aman- dan jawabannya adalah bahwa ada empat syarat yang harus benar ... Bayangkan kode berikut (dan ini terjemahan bahasa mesin)
sumber
Saya menyukai definisi Java Concurrency in Practice dari Brian Goetz untuk kelengkapannya
"Kelas adalah thread-safe jika berperilaku dengan benar ketika diakses dari beberapa utas, terlepas dari penjadwalan atau interleaving dari pelaksanaan utas tersebut oleh lingkungan runtime, dan tanpa sinkronisasi tambahan atau koordinasi lainnya pada bagian dari kode panggilan. "
sumber
Seperti yang telah ditunjukkan oleh orang lain, keamanan utas berarti bahwa sepotong kode akan berfungsi tanpa kesalahan jika digunakan oleh lebih dari satu utas sekaligus.
Perlu disadari bahwa ini kadang-kadang terjadi pada biaya, waktu komputer dan pengkodean yang lebih kompleks, sehingga tidak selalu diinginkan. Jika suatu kelas dapat digunakan dengan aman hanya pada satu utas, mungkin lebih baik melakukannya.
Sebagai contoh, Java memiliki dua kelas yang hampir setara,
StringBuffer
danStringBuilder
. Perbedaannya adalahStringBuffer
thread-safe, jadi instance tunggal dariStringBuffer
dapat digunakan oleh banyak utas sekaligus.StringBuilder
bukan thread-safe, dan dirancang sebagai pengganti berkinerja tinggi untuk kasus-kasus tersebut (sebagian besar) ketika String dibangun hanya oleh satu utas.sumber
Thread-safe-code berfungsi seperti yang ditentukan, bahkan ketika dimasukkan secara bersamaan oleh utas berbeda. Ini sering berarti, bahwa struktur data internal atau operasi yang harus dijalankan tanpa gangguan dilindungi terhadap modifikasi yang berbeda pada saat yang sama.
sumber
Cara yang lebih mudah untuk memahaminya, adalah apa yang membuat kode tidak aman. Ada dua masalah utama yang akan membuat aplikasi berulir memiliki perilaku yang tidak diinginkan.
Mengakses variabel bersama tanpa mengunci
Variabel ini dapat dimodifikasi oleh utas lainnya saat menjalankan fungsi. Anda ingin mencegahnya dengan mekanisme penguncian untuk memastikan perilaku fungsi Anda. Aturan umum adalah untuk menjaga kunci untuk waktu sesingkat mungkin.
Jalan buntu yang disebabkan oleh saling ketergantungan pada variabel bersama.
Jika Anda memiliki dua variabel bersama A dan B. Dalam satu fungsi, Anda mengunci A terlebih dahulu kemudian Anda mengunci B. Dalam fungsi lain, Anda mulai mengunci B dan setelah beberapa saat, Anda mengunci A. Ini adalah kebuntuan potensial di mana fungsi pertama akan menunggu B untuk dibuka ketika fungsi kedua akan menunggu A dibuka. Masalah ini mungkin tidak akan terjadi di lingkungan pengembangan Anda dan hanya dari waktu ke waktu. Untuk menghindarinya, semua kunci harus selalu dalam urutan yang sama.
sumber
Iya dan tidak.
Keamanan utas sedikit lebih dari sekadar memastikan data Anda yang dibagikan diakses hanya oleh satu utas sekaligus. Anda harus memastikan akses berurutan ke data bersama, sementara pada saat yang sama menghindari kondisi balapan , kebuntuan , penghidupan , dan kelaparan sumber daya .
Hasil yang tidak dapat diprediksi saat beberapa utas berjalan bukanlah kondisi yang diperlukan dari kode aman-ulir, tetapi sering merupakan produk sampingan. Misalnya, Anda dapat mengatur skema produsen-konsumen dengan antrian bersama, satu utas produsen, dan beberapa utas konsumen, dan aliran data mungkin dapat diprediksi dengan sempurna. Jika Anda mulai memperkenalkan lebih banyak konsumen, Anda akan melihat hasil yang terlihat lebih acak.
sumber
Pada dasarnya, banyak hal bisa salah dalam lingkungan multi-ulir (instruksi menata ulang, objek yang dibangun sebagian, variabel yang sama memiliki nilai yang berbeda di utas yang berbeda karena caching di level CPU dll.).
Saya suka definisi yang diberikan oleh Java Concurrency in Practice :
Yang mereka maksud dengan benar adalah program berperilaku sesuai dengan spesifikasinya.
Contoh buatan
Bayangkan Anda menerapkan penghitung. Anda dapat mengatakan bahwa itu berlaku dengan benar jika:
counter.next()
jangan pernah mengembalikan nilai yang telah dikembalikan sebelumnya (kami menganggap tidak ada luapan, dll. untuk kesederhanaan)Penghitung thread aman akan berperilaku sesuai dengan aturan itu terlepas dari berapa banyak thread mengaksesnya secara bersamaan (yang biasanya tidak akan menjadi kasus implementasi yang naif).
Catatan: lintas pos pada Programmer
sumber
Cukup - kode akan berjalan dengan baik jika banyak utas yang mengeksekusi kode ini secara bersamaan.
sumber
Jangan bingung keselamatan thread dengan determinisme. Kode thread-safe juga bisa non-deterministik. Mengingat sulitnya masalah debugging dengan kode berulir, ini mungkin merupakan kasus normal. :-)
Keamanan utas hanya memastikan bahwa ketika utas memodifikasi atau membaca data yang dibagikan, tidak ada utas lain yang dapat mengaksesnya dengan cara yang mengubah data. Jika kode Anda bergantung pada urutan tertentu untuk eksekusi demi kebenaran, maka Anda memerlukan mekanisme sinkronisasi lain selain yang diperlukan untuk keamanan utas untuk memastikan hal ini.
sumber
Saya ingin menambahkan lebih banyak info di atas jawaban baik lainnya.
Keamanan thread menyiratkan beberapa utas dapat menulis / membaca data dalam objek yang sama tanpa kesalahan memori yang tidak konsisten. Dalam program yang sangat multi-threaded, program yang aman thread tidak menyebabkan efek samping untuk data bersama .
Lihat pertanyaan SE ini untuk lebih jelasnya:
Apa maksud dari threadsafe?
Program thread aman menjamin konsistensi memori .
Dari halaman dokumentasi oracle pada API bersamaan lanjutan:
Properti Konsistensi Memori:
Bab 17 dari Spesifikasi Bahasa Java ™ mendefinisikan hubungan yang terjadi sebelum operasi memori seperti membaca dan menulis variabel bersama. Hasil penulisan oleh satu utas dijamin akan terlihat oleh pembacaan oleh utas lain hanya jika operasi penulisan terjadi sebelum operasi baca .
The
synchronized
danvolatile
konstruksi, sertaThread.start()
danThread.join()
metode, bentuk kaleng terjadi-sebelum hubungan.Metode semua kelas di dalam
java.util.concurrent
dan sub paketnya memperpanjang jaminan ini untuk sinkronisasi tingkat yang lebih tinggi. Khususnya:Runnable
ke yangExecutor
terjadi sebelum eksekusi dimulai. Demikian pula untuk Callable yang dikirimkan keExecutorService
.Future
tindakan sebelum terjadi setelah pengambilan hasil melaluiFuture.get()
di utas lainnya.Lock.unlock, Semaphore.release, and CountDownLatch.countDown
tindakan yang terjadi sebelum tindakan yang berhasil "memperoleh" metode sepertiLock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await
pada objek sinkronisasi yang sama di utas lain.Exchanger
, tindakan sebelum diexchange()
dalam setiap utas terjadi sebelum sebelum yang terkait dengan pertukaran yang sesuai () di utas lain.CyclicBarrier.await
danPhaser.awaitAdvance
(serta variannya) terjadi-sebelum tindakan dilakukan oleh tindakan penghalang, dan tindakan yang dilakukan oleh tindakan penghalang terjadi-sebelum tindakan setelah pengembalian yang berhasil dari yang sesuai menunggu di utas lainnya.sumber
Untuk melengkapi jawaban lain:
Sinkronisasi hanya khawatir ketika kode dalam metode Anda melakukan satu dari dua hal:
Ini berarti bahwa variabel yang didefinisikan dalam metode Anda selalu aman. Setiap panggilan ke suatu metode memiliki versi variabel-variabel ini sendiri. Jika metode ini dipanggil oleh utas lain, atau oleh utas yang sama, atau bahkan jika metode itu menyebut dirinya sendiri (rekursi), nilai-nilai variabel ini tidak dibagi.
Penjadwalan utas tidak dijamin bulat-robin . Suatu tugas dapat sepenuhnya memakan CPU dengan mengorbankan utas dengan prioritas yang sama. Anda dapat menggunakan Thread.yield () untuk memiliki hati nurani. Anda dapat menggunakan (dalam java) Thread.setPriority (Thread.NORM_PRIORITY-1) untuk menurunkan prioritas utas
Selain itu waspadai:
sumber
Ya dan ya. Ini menyiratkan bahwa data tidak dimodifikasi oleh lebih dari satu utas secara bersamaan. Namun, program Anda mungkin berfungsi seperti yang diharapkan, dan tampaknya aman, meskipun pada dasarnya tidak.
Perhatikan bahwa hasil yang tidak dapat diprediksi adalah konsekuensi dari 'kondisi ras' yang mungkin menghasilkan data yang dimodifikasi dalam urutan selain yang diharapkan.
sumber
Mari kita jawab ini dengan contoh:
The
countTo10
Metode menambahkan satu ke meja dan kemudian mengembalikan nilai true jika count telah mencapai 10. Ini hanya harus kembali sekali benar.Ini akan berfungsi selama hanya satu utas yang menjalankan kode. Jika dua utas menjalankan kode pada saat yang sama berbagai masalah dapat terjadi.
Misalnya, jika hitungan dimulai sebagai 9, satu utas dapat menambahkan 1 untuk dihitung (menghasilkan 10) tetapi kemudian utas kedua dapat memasukkan metode dan menambahkan 1 lagi (membuat 11) sebelum utas pertama memiliki kesempatan untuk melakukan perbandingan dengan 10 Kemudian kedua utas melakukan perbandingan dan menemukan bahwa hitung adalah 11 dan tidak ada yang mengembalikan true.
Jadi kode ini bukan utas yang aman.
Intinya, semua masalah multi-threading disebabkan oleh beberapa variasi masalah semacam ini.
Solusinya adalah memastikan bahwa penambahan dan perbandingan tidak dapat dipisahkan (misalnya dengan mengelilingi kedua pernyataan dengan semacam kode sinkronisasi) atau dengan menyusun solusi yang tidak memerlukan dua operasi. Kode seperti itu akan aman-thread.
sumber
Setidaknya di C ++, saya menganggap thread-safe sebagai sedikit keliru karena meninggalkan banyak nama. Agar aman utas, kode biasanya harus proaktif tentangnya. Ini umumnya bukan kualitas pasif.
Agar suatu kelas aman dari tapak, ia harus memiliki fitur "ekstra" yang menambahkan overhead. Fitur-fitur ini adalah bagian dari implementasi kelas dan secara umum, tersembunyi dari antarmuka. Yaitu, utas berbeda dapat mengakses anggota kelas mana pun tanpa harus khawatir tentang pertentangan dengan akses bersamaan oleh utas berbeda DAN dapat melakukannya dengan cara yang sangat malas, menggunakan gaya pengkodean manusia biasa yang biasa, tanpa harus melakukan semua hal sinkronisasi gila yang sudah dimasukkan ke dalam isi kode yang dipanggil.
Dan inilah mengapa sebagian orang lebih suka menggunakan istilah yang disinkronkan secara internal .
Set Terminologi
Ada tiga set terminologi utama untuk ide-ide ini yang saya temui. Yang pertama dan secara historis lebih populer (tetapi lebih buruk) adalah:
Yang kedua (dan lebih baik) adalah:
Yang ketiga adalah:
Analogi
utas aman ~ utas bukti ~ disinkronkan secara internal
Contoh dari sistem yang disinkronkan secara internal (alias thread-safe atau thread proof ) adalah restoran tempat tuan rumah menyambut Anda di pintu, dan melarang Anda mengantri. Tuan rumah adalah bagian dari mekanisme restoran untuk berurusan dengan banyak pelanggan, dan dapat menggunakan beberapa trik yang agak rumit untuk mengoptimalkan tempat duduk pelanggan yang menunggu, seperti mempertimbangkan ukuran pesta mereka, atau berapa banyak waktu yang mereka miliki. , atau bahkan mengambil reservasi melalui telepon. Restoran disinkronkan secara internal karena semua ini adalah bagian dari antarmuka untuk berinteraksi dengannya.
bukan thread-safe (tapi bagus) ~ thread yang kompatibel ~ disinkronkan secara eksternal ~ free-threaded
Misalkan Anda pergi ke bank. Ada garis, yaitu pertentangan untuk teller bank. Karena Anda bukan orang biadab, Anda menyadari bahwa hal terbaik yang harus dilakukan di tengah pertengkaran untuk sumber daya adalah mengantri seperti makhluk beradab. Tidak ada yang secara teknis membuat Anda melakukan ini. Kami harap Anda memiliki pemrograman sosial yang diperlukan untuk melakukannya sendiri. Dalam hal ini, lobi bank disinkronkan secara eksternal. Haruskah kita mengatakan bahwa itu tidak aman? itulah yang implikasinya adalah jika Anda pergi dengan benang-aman , benang-aman bipolar set terminologi. Itu bukan istilah yang sangat baik. Terminologi yang lebih baik disinkronkan secara eksternal,Lobi bank tidak mudah diakses oleh banyak pelanggan, tetapi tidak melakukan sinkronisasi dengan mereka. Pelanggan melakukannya sendiri.
Ini juga disebut "utas gratis," di mana "bebas" seperti dalam "bebas dari kutu" - atau dalam hal ini, terkunci. Nah, lebih tepatnya, sinkronisasi primitif. Itu tidak berarti kode dapat berjalan di banyak utas tanpa primitif itu. Itu hanya berarti itu tidak datang dengan mereka sudah diinstal dan terserah Anda, pengguna kode, untuk menginstalnya sendiri sesuai keinginan Anda. Memasang primitif sinkronisasi Anda sendiri bisa jadi sulit dan membutuhkan pemikiran keras tentang kode, tetapi juga dapat mengarah pada program tercepat yang memungkinkan dengan menyesuaikan Anda dengan bagaimana program dijalankan pada CPU yang saat ini diretas.
bukan utas aman (dan buruk) ~ utas permusuhan ~ tidak dapat disinkronkan
Contoh analogi sehari-hari dari sistem benang-bermusuhan adalah beberapa brengsek dengan mobil sport yang menolak untuk menggunakan penutup mata mereka dan mengubah jalur mau tak mau. Gaya mengemudi mereka tidak bersahabat atau tidak dapat dipahami karena Anda tidak memiliki cara untuk berkoordinasi dengan mereka, dan ini dapat menyebabkan pertengkaran untuk jalur yang sama, tanpa resolusi, dan dengan demikian kecelakaan ketika dua mobil berusaha untuk menempati ruang yang sama, tanpa protokol apa pun untuk cegah ini. Pola ini juga dapat dianggap lebih luas sebagai anti-sosial, yang saya sukai karena kurang spesifik untuk utas dan lebih umum berlaku untuk banyak bidang pemrograman.
Mengapa thread safe et al. adalah set terminologi yang buruk
Set terminologi pertama dan tertua gagal membuat perbedaan yang lebih baik antara permusuhan thread dan kompatibilitas thread . Kompatibilitas thread lebih pasif daripada yang disebut safety thread, tetapi itu tidak berarti kode yang dipanggil tidak aman untuk penggunaan thread bersamaan. Itu hanya berarti pasif tentang sinkronisasi yang akan memungkinkan ini, menundanya ke kode panggilan, alih-alih menyediakannya sebagai bagian dari implementasi internal. Thread kompatibel adalah bagaimana kode mungkin harus ditulis secara default dalam banyak kasus, tetapi sayangnya ini juga sering keliru dianggap sebagai thread tidak aman, seolah-olah itu inheren anti keselamatan, yang merupakan titik utama kebingungan bagi para programmer.
CATATAN: Banyak manual perangkat lunak yang benar-benar menggunakan istilah "thread-safe" untuk merujuk pada "thread-kompatibel," menambahkan lebih banyak kebingungan pada apa yang sudah berantakan! Saya menghindari istilah "thread-safe" dan "thread-safe" di semua biaya karena alasan ini, karena beberapa sumber akan menyebut sesuatu "thread-safe" sementara yang lain akan menyebutnya "thread-safe" karena mereka tidak dapat setuju apakah Anda harus memenuhi beberapa standar ekstra untuk keselamatan (primitif sinkronisasi), atau TIDAK memusuhi dianggap "aman". Jadi hindari istilah-istilah itu dan gunakan istilah yang lebih cerdas, untuk menghindari miskomunikasi berbahaya dengan insinyur lain.
Pengingat tujuan kami
Pada dasarnya, tujuan kami adalah untuk menumbangkan kekacauan.
Kami melakukannya dengan menciptakan sistem deterministik yang dapat kami andalkan. Determinisme itu mahal, sebagian besar karena biaya peluang kehilangan paralelisme, pipelining, dan pemesanan ulang. Kami mencoba meminimalkan jumlah determinisme yang kami butuhkan untuk menjaga biaya tetap rendah, sambil menghindari pengambilan keputusan yang akan mengikis lebih sedikit determinisme yang kami mampu.
Sinkronisasi utas adalah tentang meningkatkan urutan dan mengurangi kekacauan. Tingkat di mana Anda melakukan ini sesuai dengan persyaratan yang disebutkan di atas. Level tertinggi berarti bahwa suatu sistem berperilaku dengan cara yang sepenuhnya dapat diprediksi setiap saat. Tingkat kedua berarti bahwa sistem berperilaku cukup baik sehingga kode panggilan dapat dengan andal mendeteksi ketidakpastian. Misalnya, bangun palsu dalam kondisi variabel atau kegagalan untuk mengunci mutex karena tidak siap. Tingkat ketiga berarti bahwa sistem tidak berperilaku cukup baik untuk bermain dengan orang lain dan hanya bisa PERNAH dijalankan dengan single-threaded tanpa menimbulkan kekacauan.
sumber
Alih-alih menganggap kode atau kelas sebagai thread aman atau tidak, saya pikir lebih bermanfaat untuk menganggap tindakan sebagai thread-safe. Dua tindakan aman thread jika mereka akan berperilaku seperti yang ditentukan saat dijalankan dari konteks threading sewenang-wenang. Dalam banyak kasus, kelas akan mendukung beberapa kombinasi tindakan dengan cara yang aman dan lainnya tidak.
Misalnya, banyak koleksi seperti daftar-array dan set hash akan menjamin bahwa jika mereka awalnya diakses secara eksklusif dengan satu utas, dan mereka tidak pernah dimodifikasi setelah referensi menjadi terlihat pada utas lain, mereka dapat dibaca secara sewenang-wenang dengan kombinasi apa pun utas tanpa gangguan.
Lebih menarik, beberapa koleksi hash-set seperti yang non-generik asli di .NET, dapat menawarkan jaminan bahwa selama tidak ada item yang pernah dihapus, dan asalkan hanya satu utas yang pernah menulis kepada mereka, setiap utas yang mencoba untuk membaca koleksi akan berperilaku seolah-olah mengakses koleksi di mana pembaruan mungkin tertunda dan terjadi dalam urutan sewenang-wenang, tetapi yang sebaliknya akan berperilaku normal. Jika utas # 1 menambahkan X dan kemudian Y, dan utas # 2 mencari dan melihat Y dan kemudian X, akan mungkin bagi utas # 2 untuk melihat bahwa Y ada tetapi X tidak; apakah perilaku seperti itu adalah "aman-utas" akan tergantung pada apakah utas # 2 siap untuk menghadapi kemungkinan itu.
Sebagai catatan akhir, beberapa kelas - terutama memblokir perpustakaan komunikasi - mungkin memiliki metode "tutup" atau "Buang" yang aman untuk semua metode lainnya, tetapi tidak ada metode lain yang aman untuk sehubungan dengan satu sama lain. Jika utas melakukan pembacaan permintaan pemblokiran dan pengguna program mengklik "batal", tidak akan ada cara untuk menutup permintaan yang dikeluarkan oleh utas yang mencoba melakukan pembacaan. Permintaan tutup / buang, bagaimanapun, dapat secara tidak sinkron menetapkan bendera yang akan menyebabkan permintaan baca dibatalkan sesegera mungkin. Setelah penutupan dilakukan pada utas apa pun, objek akan menjadi tidak berguna, dan semua upaya tindakan di masa depan akan gagal dengan segera,
sumber
Dengan kata sederhana: P Jika aman untuk mengeksekusi banyak utas pada suatu blok kode maka amankan *
*ketentuan berlaku
Ketentuan disebutkan oleh jawaban lain seperti 1. Hasilnya harus sama jika Anda mengeksekusi satu utas atau beberapa utas di atasnya dll.
sumber