Bir muteksin kilitli olup olmadığını neden kontrol edemiyorum?


28

C ++ 14 std::mutexkilitli olup olmadığını kontrol etmek için bir mekanizma atlamış görünüyor . Bu SO soruya bakın:

https://stackoverflow.com/questions/21892934/how-to-assert-if-a-stdmutex-is-locked

Bunun etrafında birkaç yol vardır, örneğin;

std::mutex::try_lock()
std::unique_lock::owns_lock()

Ancak bunların hiçbiri özellikle tatmin edici çözümler değil.

try_lock()Geçerli iş parçacığı mutex'i kilitledi, yanlış bir negatif döndürmek için izin verilir ve tanımsız davranışa sahip. Aynı zamanda yan etkileri vardır. Orijinalin üstüne owns_lock()bir yapı gerektirir .unique_lockstd::mutex

Açıkçası kendime gelebilirdim, ancak mevcut arayüz için motivasyonları anlamayı tercih ederim.

Bir muteksin durumunu (örn. std::mutex::is_locked()) Kontrol etme yeteneği bana ezoterik bir istek gibi görünmüyor, bu yüzden Standart Komitenin gözetimden ziyade kasıtlı olarak bu özelliği atladığından şüpheleniyorum.

Niye ya?

Düzenleme: Tamam, belki de bu kullanım durumu beklediğim kadar yaygın değil, bu yüzden kendi senaryomu göstereceğim. Birden fazla iş parçacığı üzerinde dağıtılmış bir makine öğrenme algoritmasına sahibim. Her iş parçacığı eşzamansız çalışır ve bir optimizasyon sorunu tamamladıktan sonra ana havuza geri döner.

Daha sonra bir ana muteksi kilitler. İplik daha sonra, bir çocuğu mutasyona uğratmak için yeni bir ebeveyn seçmelidir, ancak yalnızca şu anda başka iplikler tarafından optimize edilmiş yavrulara sahip olmayan ebeveynlerden seçim yapabilir. Bu nedenle, şu anda başka bir iş parçacığı tarafından kilitlenmemiş ebeveynleri bulmak için bir arama yapmam gerekiyor. Ana iş parçacığı muteks kilitli olduğundan, arama sırasında değişen muteks durumunun riski yoktur. Açıkçası başka çözümler var (şu anda bir boolean bayrağı kullanıyorum) ancak mutex'in dişler arası senkronizasyon amacıyla olduğu gibi bu soruna mantıklı bir çözüm sunduğunu düşündüm.


42
Bir muteksin kilitli olup olmadığını gerçekten makul bir şekilde kontrol edemezsiniz, çünkü kontrolten sonra bir nanosaniyenin kilidi açılabilir veya kilitlenebilir. Yani eğer "if (mutex_is_locked ()) ..." yazdıysanız, mutex_is_locked doğru sonucu verebilir, ancak "if" çalıştırıldığında yanlış olur.
gnasher729 11:16

1
Bu ^. Hangi yararlı bilgileri almayı umuyorsunuz is_locked?
İşe yaramaz

3
Bu bir XY problemi gibi geliyor. Neden ebeveynlerin yalnızca bir çocuk oluşturulurken yeniden kullanılmasını önlemeye çalışıyorsunuz? Herhangi bir ebeveynin sadece bir çocuğa sahip olması gerekliliği var mı? Kilitin bunu engellemeyecek. Net nesilleriniz yok mu? Olmazsa, daha hızlı optimize edilebilen bireylerin daha sık / erken seçilebildiklerinden daha fazla uygunluğa sahip olduklarının farkında mısınız? Nesiller kullanıyorsanız, neden tüm ebeveynleri önden seçmiyorsunuz, o zaman iş parçacığı bir kuyruktan ebeveynleri alsın? Yavrular üretmek gerçekten o kadar pahalı mı, birden fazla konuya ihtiyacınız var mı?
amon

10
@quant - Örnek uygulamanızdaki ana nesne mutekslerinizin neden muteksler olması gerektiğini anlamıyorum: ayarlandıklarında kilitlenen ana bir muteksiniz varsa, durumlarını belirtmek için yalnızca bir boole değişkeni kullanabilirsiniz.
Periata Breatta 11:16

4
Sorunun son cümlesine katılmıyorum. Basit bir boolean değeri burada muteks'ten çok daha temizdir. Bir ebeveyni "döndürmek" için ana muteksi kilitlemek istemiyorsanız, atomik bir bool yapın.
Sebastian Redl

Yanıtlar:


53

Önerilen operasyonda en az iki ciddi problem görebiliyorum.

İlki, bir yorumda @ gnasher729 tarafından zaten belirtildi :

Bir muteksin kilitli olup olmadığını gerçekten makul bir şekilde kontrol edemezsiniz, çünkü kontrolten sonra bir nanosaniyenin kilidi açılabilir veya kilitlenebilir. Yazdığınız Yani eğer if (mutex_is_locked ()) …o zaman mutex_is_lockeddoğru sonucu döndürür, ancak zamanla olabilir ifyürütüldüğünde, bu yanlış.

Bir muteksin “şu anda kilitli” özelliğinin değişmediğinden emin olmanın tek yolu, kendiniz kilitlemenizdir.

Gördüğüm ikinci sorun, bir muteksi kilitlemediğiniz sürece, iş parçanızın daha önce muteksi kilitleyen iş parçacığıyla eşitlemediğidir. Bu nedenle, “önce” ve “sonra” hakkında konuşmak ve muteksin kilitli olup olmadığını bile iyi tanımlamamışsınız bile, Schrödiger'in kedisinin şu anda kutuyu açmadan hayatta olup olmadığını sormak gibi bir şey.

Doğru anlarsam, o zaman kilitli olan master mutex sayesinde her iki problem de kendi durumunuzda tartışılır. Ancak bu bana çok yaygın bir durum gibi görünmüyor, bu yüzden komitenin çok özel senaryolarda biraz faydalı olabilecek ve diğerlerinde hasara yol açabilecek bir fonksiyon ekleyerek doğru olanı yaptığını düşünüyorum. (“Arayüzlerin doğru kullanımı kolay ve yanlış kullanımı zor” ruhu ile.)

Ve eğer söyleyebilseydim, şu an sahip olduğunuz kurulumun en şık olmadığını ve problemi tamamen önlemek için yeniden yapılandırılabileceğini düşünüyorum. Örneğin, şu anda kilitlenmemiş olan tüm potansiyel ebeveynleri denetleyen ana iş parçacığı yerine, neden hazır ebeveynlerin sırasını korumuyorsunuz? Bir iş parçacığı bir başkasını optimize etmek isterse, bir diğerini sıradan çıkarır ve yeni ana babaları olur olmaz onları sıraya ekler. Bu şekilde, bir koordinatör olarak ana iş parçasına bile ihtiyacınız yoktur.


Teşekkürler, bu iyi bir cevap. Hazır ebeveynlerin sırasını korumak istemememin nedeni, ebeveynlerin yaratılma sırasını korumam gerektiğidir (bu onların ömrünü belirlediği için). Bu bir LIFO kuyruğu ile kolayca yapılabilir. İçeri girip çıkmaya başlarsam, işleri karmaşık hale getirecek ayrı bir sipariş mekanizmasını sürdürmek zorunda kalacağım, dolayısıyla şu anki yaklaşım.
Quant

14
@quant: Ebeveynleri sıraya koymak için iki amacınız varsa, bunu iki sıra ile yapabilirsiniz ...

@quant: Bir öğeyi (en fazla) bir kez siliyorsunuz, ancak her seferinde birden fazla işlem yapıyor olabilirsiniz, bu nedenle nadir vakayı, genel vakanın pahasına pahasına optimize edersiniz. Bu nadiren arzu edilir.
Jerry Coffin

2
Ama olan şimdiki evrenin muteksi kilitli olup olmadığını sormak için makul.
Sınırlı Kefaret

@LimitedAtonement Gerçekten değil. Bunu yapmak için mutex ek bilgileri (iş parçacığı kimliği) depolamak zorunda bırakarak yavaşlatır. Özyinelemeli muteksler zaten bunu yapar, onun yerine siz yapmalısınız.
StaceyGirl

9

İkincil muteksleri, bir optimizasyon problemine erişimi kilitlemek için değil, bir optimizasyon probleminin şu anda optimize edilip edilmediğini belirlemek için kullandığınız görülüyor.

Bu tamamen gereksiz. İyileştirilmesi gereken sorunların bir listesi, şu anda optimize edilen sorunların bir listesi ve optimize edilmiş bir sorunların listesi olurdu. (Kelimenin tam anlamıyla "liste" almayın, uygun herhangi bir veri yapısı "anlamına gelsin").

Zamanlanmamış problemler listesine yeni bir problem ekleme veya problemi bir listeden diğerine taşıma işlemleri, "master" mutex muteksinin korunması altında gerçekleştirilecektir.


1
std::mutexBöyle bir veri yapısı için bir tür nesnenin uygun olduğunu düşünmüyor musunuz?
Quant

2
@quant - hayır. std::mutexTahsis edilmesi ve / veya kullanılması için kısıtlı ve yavaş olan kaynakları iyi alabilecek bir işletim sistemi tarafından tanımlanmış muteks uygulamasına dayanır. Dahili bir veri yapısına erişimi kilitlemek için tek bir muteks kullanmak, çok daha verimli ve muhtemelen daha fazla ölçeklenebilir olabilir.
Periata Breatta 11:16

1
Ayrıca Koşul Değişkenlerini de göz önünde bulundurun. Bunun gibi birçok veri yapısını gerçekten kolaylaştırabilirler.
Cort Ammon - Monica

2

Diğerlerinin de söylediği gibi, is_lockedbir muteksin üzerinde herhangi bir yararın olduğu bir durum yoktur , bu yüzden işlev yoktur.

En sık karşılaşılan iş parçacığı uygulaması değilse , sorunla karşılaştığınız durum inanılmaz derecede yaygındır, temel olarak çalışan iş parçacıklarının yaptığı, bunlardan biri .

Üzerinde 10 kutu bulunan bir rafınız var. Bu kutularla çalışan 4 işçiniz var. 4 işçinin farklı kutular üzerinde çalıştığından nasıl emin olabilirsiniz? İlk çalışan, üzerinde çalışmaya başlamadan önce raftan bir kutu çıkarır. İkinci işçi rafta 9 kutu görüyor.

Kutuları kilitlemek için hiçbir muteks yoktur, bu nedenle kutudaki hayali muteksin durumunu görmek gerekmez ve bir muteksin bir boole olarak kötüye kullanılması tam anlamıyla yanlıştır. Mutex rafı kilitler.


1

Yukarıda 5gon12eder'in cevabında verilen iki nedenin yanı sıra, bunun ne gerekli ne de arzu edildiğini de eklemek isterim.

Zaten bir muteks tutuyorsan, o zaman elinde tuttuğunu daha iyi bildin! Sormana gerek yok. Tıpkı bir bellek bloğuna veya başka bir kaynağa sahip olmak gibi, tam olarak sizde olup olmadığını ve kaynağı serbest bırakmak / silmek uygun olduğunda bilmelisiniz.
Aksi takdirde, programınız kötü tasarlanır ve başınız belaya giriyor.

Mutex tarafından korunan paylaşılan kaynağa erişmeniz gerekiyorsa ve zaten muteks'i tutmuyorsanız, muteks'i edinmeniz gerekir. Başka bir seçenek yok, aksi halde program mantığınız doğru değil.
Her iki durumda lock()da engellemeyi kabul edilebilir veya kabul edilemez bulursunuz veya try_lock()istediğiniz davranışı verirsiniz. Bilmeniz gereken tek şey, olumlu ve şüphesiz mutex'i başarılı bir şekilde elde edip etmediğinizdir (dönüş değeritry_lock ). Başkasının elinde tutması ya da sahte bir başarısızlık olup olmadığına bakmanız önemsizdir.

Diğer her durumda, açıkça, bu seni ilgilendirmez. Bilmeniz gerekmez, bilmemelisiniz veya varsayımlarda bulunmalısınız (diğer soruda belirtilen zamanlama ve senkronizasyon sorunları için).


1
Kilitleme için şu anda mevcut olan kaynaklar üzerinde bir sıralama işlemi yapmak istersem ne olur?
Quant

Ama bu gerçekleşmesi gerçekçi olan bir şey mi? Bunu alışılmadık görürüm. Her iki kaynağın da kendine özgü bir sıralama türüne sahip olduğunu söyleyebilirim, o zaman ilk önce daha önemli olanı yapmanız gerekir (kilit için). Örnek: Oluşturmadan önce fizik simülasyonunu güncellemek gerekir. Veya, sıralama daha fazla veya daha az kasıtlı, o zaman try_lockilk kaynağı da yapabilirsiniz ve bu başarısız olursa, ikincisini deneyin. Örnek: Veritabanı sunucusuna üç kalıcı, birleştirilmiş bağlantı ve bir komut göndermek için bir bağlantı kullanmanız gerekir.
Damon,

4
@quant - "şu anda kilitlemeye hazır olan kaynaklar üzerinde bir sıralama işlemi" - genel olarak, bu tür bir şeyi yapmak, çözüme kavuşturmak için zorla girdiğiniz bir kod yazmak için kolay ve hızlı bir yoldur. Kilit satın alma ve serbest bırakma deterministik yapmak, hemen hemen her durumda en iyi politikadır. Değişebilecek bir kritere göre bir kilit aramak sorun istiyor.
Periata Breatta 11:16

@PeriataBreatta Programım kasıtlı olarak belirsiz. Şimdi bu özniteliğin yaygın olmadığını görüyorum, bu yüzden is_locked()bu tür davranışları kolaylaştırabilecek özelliklerin eksik olduğunu anlayabiliyorum.
Quant

@quant sıralama ve kilitleme tamamen ayrı problemlerdir. Kilidi olan bir kuyruğu bir şekilde sıralamak veya yeniden sıralamak istiyorsanız, kilitleyin, sıralayın ve ardından kilidini açın. İhtiyacınız olursa is_locked, o zaman aklınızdaki sorundan çok daha iyi bir çözüm var.
Peter,

1

Atomic_flag dosyasını varsayılan bellek sırasına göre kullanmak isteyebilirsiniz . Veri yarışlarına sahip değildir ve mutex gibi çoklu kilit açma çağrıları gibi istisnalar asla atmaz (ve kontrol edilemez şekilde iptal eder, ekleyebilirim ...). Alternatif olarak, load ve compare_exchange_strong gibi güzel fonksiyonlara sahip atomik (örneğin, atomik [bool] veya atomik [int] (üçgen parantezli [[] değil)) vardır.


1

Bunun için bir kullanım durumu eklemek istiyorum: Arayanın gerçekten kilidi tuttuğunu ön şart / iddia olarak sağlamak için dahili bir işlevin etkinleştirilmesini sağlayacaktır.

Bu kadar iç işlevi olan ve muhtemelen bunları çağıran birçok kamu işlevine sahip sınıflar için, iç işlevini çağıran başka bir kamu işlevi ekleyen birinin gerçekten kilidi alması sağlanabilir.

class SynchronizedClass
{

public:

void publicFunc()
{
  std::lock_guard<std::mutex>(_mutex);

  internalFuncA();
}

// A lot of code

void newPublicFunc()
{
  internalFuncA(); // whops, forgot to acquire the lock
}


private:

void internalFuncA()
{
  assert(_mutex.is_locked_by_this_thread());

  doStuffWithLockedResource();
}

};
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.