Apakah "balapan data" dan "kondisi balapan" sebenarnya adalah hal yang sama dalam konteks pemrograman bersamaan

Jawaban:

142

Tidak, mereka bukanlah hal yang sama. Mereka bukan bagian dari satu sama lain. Mereka juga tidak diperlukan, atau kondisi yang cukup untuk satu sama lain.

Definisi balapan data cukup jelas, dan oleh karena itu, penemuannya dapat diotomatiskan. Sebuah ras data terjadi ketika 2 instruksi dari benang yang berbeda mengakses lokasi memori yang sama, setidaknya satu dari akses ini adalah menulis dan tidak ada sinkronisasi yang mewajibkan setiap urutan tertentu antara akses tersebut.

Kondisi balapan adalah kesalahan semantik. Ini adalah kesalahan yang terjadi dalam pengaturan waktu atau urutan kejadian yang mengarah pada perilaku program yang salah. Banyak kondisi balapan dapat disebabkan oleh data race, tetapi ini tidak perlu.

Pertimbangkan contoh sederhana berikut di mana x adalah variabel bersama:

Thread 1    Thread 2

 lock(l)     lock(l)
 x=1         x=2
 unlock(l)   unlock(l)

Dalam contoh ini, penulisan ke x dari utas 1 dan 2 dilindungi oleh kunci, oleh karena itu penulisan ini selalu terjadi dalam urutan tertentu yang diberlakukan oleh urutan perolehan kunci pada waktu proses. Artinya, tulisan 'atomicity tidak dapat dipatahkan; selalu ada yang terjadi sebelum hubungan antara dua penulisan dalam eksekusi apa pun. Kita tidak bisa tahu tulisan mana yang terjadi sebelum yang lain apriori.

Tidak ada urutan tetap antara penulisan, karena locks tidak dapat menyediakan ini. Jika ketepatan program dikompromikan, katakanlah ketika tulis ke x oleh utas 2 diikuti oleh tulis ke x di utas 1, kita katakan ada kondisi balapan, meskipun secara teknis tidak ada balapan data.

Jauh lebih berguna untuk mendeteksi kondisi balapan daripada data race; namun ini juga sangat sulit untuk dicapai.

Menyusun contoh kebalikannya juga sepele. Posting blog ini juga menjelaskan perbedaannya dengan sangat baik, dengan contoh transaksi bank sederhana.

Baris Kasikci
sumber
1
"data race (...) tidak ada sinkronisasi yang mewajibkan urutan tertentu di antara akses ini." Saya sedikit bingung. Dalam contoh Anda, operasi mungkin terjadi di kedua pesanan (baik = 1 lalu = 2, atau sebaliknya). Mengapa ini bukan data race?
josinalvo
6
@josinalvo: ini adalah artefak dari definisi teknis dari data race. Poin utamanya adalah bahwa di antara dua akses, akan ada pelepasan kunci dan perolehan kunci (untuk salah satu dari pesanan yang memungkinkan). Menurut definisi, pelepasan kunci dan perolehan kunci menetapkan urutan antara dua akses, dan oleh karena itu tidak ada perlombaan data.
Baris Kasikci
Sinkronisasi tidak pernah mengamanatkan urutan tertentu antara operasi, jadi ini bukan cara yang sangat menguntungkan untuk mengekspresikannya. Di sisi lain, JMM menetapkan bahwa untuk setiap operasi baca harus ada operasi tulis tertentu yang diamati, bahkan dalam data race. Sulit untuk menghindari penyebutan terjadi-sebelum dan urutan sinkronisasi secara eksplisit , namun definisi JLS pun salah dalam menyebutkan baru saja terjadi : menurut definisinya, dua penulisan volatil yang bersamaan merupakan perlombaan data.
Marko Topolnik
@BarisKasikci "menetapkan pesanan" tidak memiliki arti yang sebenarnya, sejauh yang saya ketahui. Itu hanya kata-kata musang. Sejujurnya saya tidak percaya "data race" adalah konsep yang berguna dari jarak jauh, karena secara harfiah setiap lokasi memori yang diakses oleh beberapa utas dapat dianggap berada dalam "balapan data"
Noldorin
Pasangan lepas-ambil selalu membuat pesanan. Penjelasan umum sangat panjang, tetapi contoh sepele adalah pasangan sinyal-tunggu. @Noldorin "Menetapkan pesanan" mengacu pada pesanan yang terjadi sebelum, yang merupakan konsep kunci dari teori konkurensi (lihat makalah mani Lamport tentang hubungan yang terjadi sebelum) dan sistem terdistribusi. Data race adalah konsep yang berguna, karena kehadirannya menimbulkan banyak masalah (misalnya, semantik yang tidak ditentukan sesuai model memori C ++, semantik yang sangat kompleks di Java, dll.). Deteksi dan eliminasi mereka merupakan literatur yang luas dalam penelitian dan praktik.
Baris Kasikci
20

Menurut Wikipedia, istilah "kondisi balapan" telah digunakan sejak zaman gerbang logika elektronik pertama. Dalam konteks Java, kondisi balapan dapat berkaitan dengan sumber daya apa pun, seperti file, koneksi jaringan, utas dari kumpulan utas, dll.

Istilah "data race" paling baik digunakan untuk arti spesifiknya yang ditentukan oleh JLS .

Kasus yang paling menarik adalah kondisi balapan yang sangat mirip dengan data race, tetapi tetap tidak sama, seperti dalam contoh sederhana ini:

class Race {
  static volatile int i;
  static int uniqueInt() { return i++; }
}

Karena itidak stabil, tidak ada perlombaan data; namun, dari sudut pandang ketepatan program ada kondisi perlombaan karena non-atomicity dari dua operasi: baca i, tulis i+1. Beberapa utas dapat menerima nilai yang sama dari uniqueInt.

Marko Topolnik
sumber
1
dapatkah Anda memasukkan jawaban Anda ke dalam baris yang menjelaskan apa yang data racesebenarnya berarti di JLS?
Geek
@geek Kata "JLS" adalah hyperlink ke bagian yang relevan di JLS.
Marko Topolnik
@MarkoTopolnik Saya sedikit bingung dengan contohnya. Bisakah Anda menjelaskan: "Karena saya tidak stabil, tidak ada data race"? Voltilitas hanya memastikan bahwa itu terlihat tetapi masih: 1) tidak tersinkronisasi dan beberapa utas dapat membaca / menulis pada saat yang sama dan 2) Ini dibagi bidang non-final Jadi, menurut Java Concurrency in Practice (dikutip di bawah juga) , ini adalah data race dan bukan kondisi balapan, bukan?
aniliitb10
@ aniliitb10 Daripada mengandalkan kutipan tangan kedua yang diambil dari konteksnya, Anda harus meninjau JLS bagian 17.4 yang saya tautkan dalam jawaban saya. Akses ke variabel volatil adalah tindakan sinkronisasi seperti yang didefinisikan dalam §17.4.2.
Marko Topolnik
@ aniliitb10 Votaltiles tidak menyebabkan perlombaan data, karena aksesnya dapat dipesan. Artinya, Anda dapat menalar urutan mereka dengan cara ini atau itu, yang mengarah pada hasil yang berbeda. Dengan data race, Anda tidak memiliki alasan untuk menentukan urutannya. Misalnya, operasi i ++ dari setiap utas mungkin saja terjadi pada nilai cache lokal masing-masing i. Secara global Anda tidak memiliki cara untuk memesan operasi tersebut (dari sudut pandang programmer) - kecuali Anda memiliki model memori bahasa tertentu.
Xiao-Feng Li
3

Tidak, mereka berbeda & tidak satupun dari mereka merupakan bagian dari satu atau sebaliknya.

Istilah kondisi balapan sering kali disalahartikan dengan istilah terkait data race, yang muncul saat sinkronisasi tidak digunakan untuk mengoordinasikan semua akses ke kolom nonfinal bersama. Anda mengambil risiko perlombaan data setiap kali utas menulis variabel yang selanjutnya mungkin dibaca oleh utas lain atau membaca variabel yang mungkin terakhir ditulis oleh utas lain jika kedua utas tidak menggunakan sinkronisasi; kode dengan balapan data tidak memiliki semantik yang ditentukan berguna di bawah Model Memori Java. Tidak semua kondisi balapan adalah balapan data, dan tidak semua balapan data adalah kondisi balapan, tetapi keduanya dapat menyebabkan program bersamaan gagal dengan cara yang tidak terduga.

Diambil dari buku luar biasa - Java Concurrency in Practice oleh Joshua Bloch & Co.

Shirgill Farhan
sumber
Perhatikan bahwa pertanyaan tersebut memiliki tag bahasa-agnostik.
martinkunev
1

TL; DR: Perbedaan antara data race dan kondisi ras bergantung pada sifat perumusan masalah, dan di mana harus menggambarkan batas antara perilaku tidak terdefinisi dan perilaku yang terdefinisi dengan baik namun tidak pasti. Perbedaan saat ini bersifat konvensional dan paling mencerminkan antarmuka antara arsitek prosesor dan bahasa pemrograman.

1. Semantik

Perlombaan data secara khusus mengacu pada "akses memori" (atau tindakan, atau operasi) konflik yang tidak disinkronkan ke lokasi memori yang sama. Jika tidak ada konflik dalam akses memori, sementara masih ada perilaku tak tentu yang disebabkan oleh pengurutan operasi, itu adalah kondisi balapan.

Catatan "akses memori" di sini memiliki arti khusus. Mereka mengacu pada tindakan pemuatan atau penyimpanan memori "murni", tanpa semantik tambahan apa pun yang diterapkan. Misalnya, penyimpanan memori dari satu utas tidak (harus) tahu berapa lama data yang akan ditulis ke dalam memori, dan akhirnya menyebar ke utas lain. Untuk contoh lain, penyimpanan memori ke satu lokasi sebelum penyimpanan lain ke lokasi lain dengan utas yang sama tidak (harus) menjamin data pertama yang ditulis dalam memori berada di depan yang kedua. Akibatnya, urutan akses memori murni tersebut tidak (harus) dapat "masuk akal" , dan apa pun bisa terjadi, kecuali ditentukan lain dengan baik.

Ketika "akses memori" didefinisikan dengan baik dalam hal pengurutan melalui sinkronisasi, semantik tambahan dapat memastikan bahwa, bahkan jika waktu akses memori tidak dapat ditentukan, urutannya dapat " dipikirkan " melalui sinkronisasi. Catatan, meskipun urutan antara akses memori dapat dilakukan dengan alasan, mereka belum tentu ditentukan, oleh karena itu kondisi balapannya.

2. Mengapa ada perbedaan?

Namun jika urutannya masih belum tentu dalam kondisi balapan, kenapa repot-repot membedakannya dengan data race? Alasannya lebih praktis daripada teoritis. Itu karena perbedaan memang ada pada antarmuka antara bahasa pemrograman dan arsitektur prosesor.

Instruksi pemuatan / penyimpanan memori dalam arsitektur modern biasanya diimplementasikan sebagai akses memori "murni", karena sifat pipeline yang tidak teratur, spekulasi, cache multi-level, interkoneksi cpu-ram, terutama multi-core, dll. Ada banyak faktor yang menyebabkan waktu dan urutan tidak pasti. Untuk menegakkan pengurutan untuk setiap instruksi memori menimbulkan hukuman yang sangat besar, terutama pada desain prosesor yang mendukung multi-core. Jadi semantik pemesanan dilengkapi dengan instruksi tambahan seperti berbagai penghalang (atau pagar).

Data race adalah situasi eksekusi instruksi prosesor tanpa pagar tambahan untuk membantu menalar urutan akses memori yang bentrok. Hasilnya tidak hanya tidak pasti, tetapi juga mungkin sangat aneh, misalnya, dua penulisan ke lokasi kata yang sama dengan utas yang berbeda dapat menghasilkan setiap tulisan setengah dari kata, atau mungkin hanya beroperasi pada nilai cache lokal mereka. - Ini adalah perilaku yang tidak terdefinisi, dari sudut pandang programmer. Tapi mereka (biasanya) didefinisikan dengan baik dari sudut pandang arsitek prosesor.

Programmer harus memiliki cara untuk alasan eksekusi kode mereka. Data race adalah sesuatu yang tidak masuk akal, oleh karena itu harus selalu dihindari (biasanya). Itulah mengapa spesifikasi bahasa yang levelnya cukup rendah biasanya mendefinisikan data race sebagai perilaku tidak terdefinisi, berbeda dari perilaku memori kondisi ras yang terdefinisi dengan baik.

3. Model memori bahasa

Prosesor yang berbeda mungkin memiliki perilaku akses memori yang berbeda, yaitu model memori prosesor. Sangat canggung bagi programmer untuk mempelajari model memori dari setiap prosesor modern dan kemudian mengembangkan program yang dapat memanfaatkannya. Diinginkan jika bahasa dapat menentukan model memori sehingga program dari bahasa tersebut selalu berperilaku seperti yang diharapkan seperti yang ditentukan oleh model memori. Itulah mengapa Java dan C ++ memiliki model memorinya yang ditentukan. Ini adalah beban pengembang kompilator / waktu proses untuk memastikan model memori bahasa diterapkan di seluruh arsitektur prosesor yang berbeda.

Artinya, jika suatu bahasa tidak ingin mengekspos perilaku prosesor tingkat rendah (dan bersedia mengorbankan keunggulan kinerja tertentu dari arsitektur modern), mereka dapat memilih untuk menentukan model memori yang sepenuhnya menyembunyikan detail "murni" memori mengakses, tetapi menerapkan semantik pengurutan untuk semua operasi memorinya. Kemudian pengembang compiler / runtime dapat memilih untuk memperlakukan setiap variabel memori sebagai volatile di semua arsitektur prosesor. Untuk bahasa ini (yang mendukung memori bersama di seluruh utas), tidak ada balapan data, tetapi mungkin masih berupa kondisi balapan, bahkan dengan bahasa dengan konsistensi urutan lengkap.

Di sisi lain, model memori prosesor bisa lebih ketat (atau kurang santai, atau pada level yang lebih tinggi), misalnya, menerapkan konsistensi sekuensial seperti yang dilakukan prosesor awal. Kemudian semua operasi memori diurutkan, dan tidak ada balapan data untuk bahasa apa pun yang berjalan di prosesor.

4. Kesimpulan

Kembali ke pertanyaan awal, IMHO boleh saja mendefinisikan data race sebagai kasus khusus dari kondisi balapan, dan kondisi balapan di satu level dapat menjadi data race di level yang lebih tinggi. Itu tergantung pada sifat perumusan masalah, dan di mana harus menarik batas antara perilaku tidak terdefinisi dan perilaku yang terdefinisi dengan baik tetapi tidak pasti. Hanya konvensi saat ini yang mendefinisikan batasan pada antarmuka pemroses bahasa, tidak selalu berarti selalu dan harus demikian; tetapi konvensi saat ini mungkin paling mencerminkan antarmuka canggih (dan kebijaksanaan) antara arsitek prosesor dan bahasa pemrograman.

Xiao-Feng Li
sumber