C ++ 14 tampaknya telah menghilangkan mekanisme untuk memeriksa apakah suatu std::mutex
terkunci atau tidak. Lihat pertanyaan SO ini:
/programming/21892934/how-to-assert-if-a-stdmutex-is-locked
Ada beberapa cara untuk mengatasi hal ini, misalnya dengan menggunakan;
std::mutex::try_lock()
std::unique_lock::owns_lock()
Tapi tak satu pun dari ini adalah solusi yang memuaskan.
try_lock()
diizinkan untuk mengembalikan negatif palsu dan memiliki perilaku tidak terdefinisi jika utas saat ini telah mengunci mutex. Ini juga memiliki efek samping. owns_lock()
membutuhkan konstruksi unique_lock
di atas yang asli std::mutex
.
Jelas saya bisa menjalankan sendiri, tetapi saya lebih suka memahami motivasi untuk antarmuka saat ini.
Kemampuan untuk memeriksa status mutex (misalnya std::mutex::is_locked()
) tidak tampak seperti permintaan esoterik bagi saya, jadi saya curiga Komite Standar sengaja menghapus fitur ini daripada menjadi pengawasan.
Mengapa?
Sunting: Oke jadi mungkin ini kasus penggunaan tidak biasa seperti yang saya harapkan, jadi saya akan menggambarkan skenario khusus saya. Saya memiliki algoritma pembelajaran mesin yang didistribusikan pada banyak utas. Setiap utas beroperasi secara tidak sinkron, dan kembali ke kumpulan induk setelah menyelesaikan masalah optimisasi.
Ini kemudian mengunci mutex master. Utas kemudian harus memilih induk baru untuk dimutasi dari keturunan, tetapi hanya dapat memilih dari induk yang saat ini tidak memiliki keturunan yang sedang dioptimalkan oleh utas lainnya. Karena itu saya perlu melakukan pencarian untuk menemukan orang tua yang saat ini tidak dikunci oleh utas lainnya. Tidak ada risiko status mutex berubah selama pencarian, karena master thread mutex dikunci. Jelas ada solusi lain (saya saat ini menggunakan bendera boolean) tapi saya pikir mutex menawarkan solusi logis untuk masalah ini, karena ada untuk keperluan sinkronisasi antar-thread.
is_locked
?Jawaban:
Saya dapat melihat setidaknya dua masalah parah dengan operasi yang disarankan.
Yang pertama sudah disebutkan dalam komentar oleh @ gnasher729 :
Satu-satunya cara untuk memastikan bahwa properti "saat ini dikunci" dari sebuah mutex tidak berubah adalah dengan, well, kunci sendiri.
Masalah kedua yang saya lihat adalah bahwa kecuali Anda mengunci mutex, utas Anda tidak disinkronkan dengan utas yang sebelumnya mengunci mutex. Oleh karena itu, bahkan tidak didefinisikan dengan baik untuk berbicara tentang "sebelum" dan "setelah" dan apakah mutex terkunci atau tidak adalah semacam bertanya apakah kucing Schrödiger saat ini hidup tanpa berusaha membuka kotak.
Jika saya mengerti benar, maka kedua masalah akan diperdebatkan dalam kasus khusus Anda berkat master mutex yang dikunci. Tetapi ini sepertinya bukan kasus yang umum bagi saya, jadi saya pikir panitia melakukan hal yang benar dengan tidak menambahkan fungsi yang mungkin berguna dalam skenario yang sangat khusus dan menyebabkan kerusakan pada yang lain. (Dengan semangat: "Jadikan antarmuka mudah digunakan dengan benar dan salah digunakan.")
Dan jika boleh saya katakan, saya pikir setup yang Anda miliki saat ini bukan yang paling elegan dan bisa di refactored untuk menghindari masalah sama sekali. Misalnya, alih-alih master thread memeriksa semua calon orang tua untuk satu yang saat ini tidak dikunci, mengapa tidak memelihara antrian orang tua yang siap? Jika utas ingin mengoptimalkan yang lain, utas yang berikutnya akan keluar dari antrian dan segera setelah memiliki orangtua baru, ia menambahkannya ke antrian. Dengan begitu, Anda bahkan tidak perlu utas utama sebagai koordinator.
sumber
Tampaknya Anda menggunakan mutex sekunder untuk tidak mengunci akses ke masalah optimisasi, tetapi untuk menentukan apakah masalah optimisasi sedang dioptimalkan saat ini atau tidak.
Itu sama sekali tidak perlu. Saya memiliki daftar masalah yang perlu dioptimalkan, daftar masalah yang dioptimalkan saat ini, dan daftar masalah yang telah dioptimalkan. (Jangan mengambil "daftar" secara harfiah, anggap itu berarti "struktur data yang sesuai).
Operasi menambahkan masalah baru ke daftar masalah yang tidak dioptimalkan, atau memindahkan masalah dari satu daftar ke daftar berikutnya, akan dilakukan di bawah perlindungan satu "master" mutex.
sumber
std::mutex
cocok untuk struktur data seperti itu?std::mutex
bergantung pada implementasi sistem mutasi yang ditentukan oleh sistem operasi yang mungkin membutuhkan sumber daya (mis. pegangan) yang terbatas dan lambat untuk dialokasikan dan / atau beroperasi. Menggunakan satu mutex untuk mengunci akses ke struktur data internal kemungkinan akan jauh lebih efisien, dan mungkin lebih terukur juga.Seperti yang dikatakan orang lain, tidak ada kasus penggunaan di mana
is_locked
pada mutex ada manfaatnya, itu sebabnya fungsinya tidak ada.Kasus Anda mengalami masalah dengan ini sangat umum, itu dasarnya apa benang pekerja lakukan, yang merupakan salah satu, jika tidak yang pelaksanaan umum yang paling benang.
Anda memiliki rak dengan 10 kotak di atasnya. Anda memiliki 4 pekerja yang bekerja dengan kotak-kotak ini. Bagaimana Anda memastikan keempat pekerja mengerjakan kotak yang berbeda? Pekerja pertama mengambil sebuah kotak dari rak sebelum mereka mulai mengerjakannya. Pekerja kedua melihat 9 kotak di rak.
Tidak ada mutex untuk mengunci kotak, jadi melihat keadaan mutex imajiner pada kotak tidak diperlukan, dan menyalahgunakan mutex sebagai boolean adalah salah. Mutex mengunci rak.
sumber
Selain dua alasan yang diberikan dalam jawaban 5gon12eder di atas, saya ingin menambahkan bahwa itu tidak perlu atau tidak diinginkan.
Jika Anda sudah memegang mutex, maka Anda sebaiknya tahu bahwa Anda memegangnya! Anda tidak perlu bertanya. Sama seperti memiliki blok memori atau sumber daya lainnya, Anda harus tahu persis apakah Anda memilikinya atau tidak, dan kapan tepat untuk melepaskan / menghapus sumber daya tersebut.
Jika bukan itu masalahnya, program Anda dirancang dengan buruk, dan Anda menuju masalah.
Jika Anda perlu mengakses sumber daya bersama yang dilindungi oleh mutex, dan Anda belum memegang mutex, maka Anda perlu memperoleh mutex. Tidak ada pilihan lain, jika tidak logika program Anda tidak benar.
Anda mungkin menemukan pemblokiran diterima atau tidak dapat diterima, dalam kedua kasus
lock()
atautry_lock()
akan memberikan perilaku yang Anda inginkan. Yang perlu Anda ketahui, secara positif, dan tanpa keraguan, adalah apakah Anda berhasil memperoleh mutex (nilai balik daritry_lock
memberi tahu Anda). Tidak penting apakah orang lain memegangnya atau apakah Anda mendapatkan kegagalan palsu.Dalam setiap kasus lainnya, terus terang, itu bukan urusan Anda. Anda tidak perlu tahu, dan Anda seharusnya tidak tahu, atau membuat asumsi (untuk masalah ketepatan waktu dan sinkronisasi yang disebutkan dalam pertanyaan lain).
sumber
try_lock
sumber daya pertama, dan jika yang satu gagal, coba yang kedua. Contoh: Tiga koneksi yang terus-menerus dan dikumpulkan ke server database, dan Anda perlu menggunakan satu untuk mengirim perintah.is_locked()
itu yang mungkin memfasilitasi perilaku tersebut.is_locked
, maka ada solusi yang jauh lebih baik untuk masalah Anda daripada yang ada dalam pikiran Anda.Anda mungkin ingin menggunakan atomic_flag dengan urutan memori default. Itu tidak memiliki ras data dan tidak pernah melempar pengecualian seperti mutex tidak dengan beberapa panggilan pembuka kunci (dan dibatalkan secara tidak terkendali, saya dapat menambahkan ...). Atau, ada atom (mis. Atom [bool] atau atom [int] (dengan tanda kurung segitiga, bukan [])), yang memiliki fungsi bagus seperti memuat dan compare_exchange_strong.
sumber
Saya ingin menambahkan use-case untuk ini: Ini akan memungkinkan fungsi internal untuk memastikan sebagai prasyarat / pernyataan bahwa penelepon memang memegang kunci.
Untuk kelas dengan beberapa fungsi internal seperti itu, dan mungkin banyak fungsi publik memanggil mereka, itu bisa memastikan, bahwa seseorang menambahkan fungsi publik lain memanggil internal itu memang memperoleh kunci.
sumber