Kilitlenme hata ayıklama yaparken nelere dikkat ediyorsunuz?


25

Son zamanlarda, çok fazla iş parçacığı kullanan projeler üzerinde çalışıyorum. Onları tasarlarken iyi olduğumu düşünüyorum; vatansız tasarımı mümkün olduğunca kullanın, birden fazla iş parçacığının ihtiyaç duyduğu tüm kaynaklara erişimi kilitleyin, vb. İşlevsel programlama konusundaki deneyimim çok yardımcı oldu.

Ancak, başkalarının iş parçacığı kodunu okurken kafam karıştı. Şu anda bir kilitlenme hata ayıklama ve kodlama stili ve tasarım benim kişisel stilimden farklı olduğundan, potansiyel kilitlenme koşulları görmek zor bir zaman geçiriyorum.

Kilitlenme hata ayıklama yaparken nelere dikkat ediyorsunuz?


Bunu SO yerine burada soruyorum çünkü kilitlenme hata ayıklama konusunda daha genel işaretçiler istiyorum, sorunuma özel bir cevap değil.
Michael K

Düşünebildiğim stratejiler günlüğe kaydediliyor (birkaçının da belirttiği gibi), aslında kimin tarafından kilitlenmeyi beklediğinin kilitlenme grafiğini inceliyor ( bazıları için bkz. Stackoverflow.com/questions/3483094/… işaretçiler) ve ek notları kilitle (bkz. clang.llvm.org/docs/ThreadSafetyAnalysis.html ). Kodunuz olmasa bile, yazarı ek açıklamalar eklemeye ikna etmeye çalışabilirsiniz - muhtemelen hatalar bulur ve bunları (muhtemelen sizinki de dahil olmak üzere) bu süreçte düzeltirler.
Don Hatch

Yanıtlar:


23

Durum gerçek bir kilitlenme ise (yani, iki diş iki farklı kilit tutar, ancak en az bir diş diğer dişin tuttuğunu kilitlemek isterse), o zaman önce dişlilerin nasıl kilitlendiğine dair tüm önizlemeleri bırakmalısınız. Hiçbir şey varsaymamak. Baktığınız koddaki tüm yorumları kaldırmak isteyebilirsiniz, çünkü bu yorumlar doğru olmayan bir şeye inanmanıza neden olabilir. Bunu yeterince vurgulamak zor: hiçbir şey düşünmeyin.

Bundan sonra, bir iplik başka bir şeyi kilitlemeye çalıştığında hangi kilitlerin tutulacağını belirleyin. Mümkünse, bir ipliğin kilitlenmeden ters sırada açıldığından emin olun. Daha da iyisi, bir dişin bir seferde sadece bir kilit tuttuğundan emin olun.

Titizlikle bir ipliğin yürütülmesi boyunca çalışır ve tüm kilitleme olaylarını inceler. Her kilitlemede, dişlinin diğer kilitleri tutup tutmadığını ve eğer öyleyse, hangi koşullar altında, benzer bir yürütme yolu yapan başka bir dişin kilitleme olayına girebileceğini belirleyin.

Muhtemelen zaman veya para bitmeden sorunu bulamayacağınız kesindir.


4
+1 Vay, bu karamsar ... gerçi doğru değil. Tüm böcekleri bulamamanın bir anlamı var. Önerileriniz için teşekkürler!
Michael K

Bruce, "gerçek çıkmaz" karakterin benim için şaşırtıcı. İki iplik arasındaki çıkmazın her birinin diğerinin elinde tuttuğu bir kilidi beklediğini sanıyordum. Tanımınız aynı zamanda, bir kilidi tutarken, şu anda farklı bir diş tarafından tutulan ikinci bir kilit almak için bekleyen bir ipliğin varlığını da içeriyor gibi görünüyor. Bu bana çıkmaz gibi gelmiyor; bu mu??
Don Hatch

@DonHatch - Kötü bir şekilde ifade ettim. Tanımladığınız durum kilitlenme değil. Bir kilit tutma A kilidi içeren bir durumda hata ayıklama, ardından kilitleme B'yi almaya çalışırken, kilitleme kilitleme B'yi tutan iş parçacığı kilitleme A'yı almaya çalışırken bir hata ayıklama mesajını iletmeyi ummuştum. Belki. Ya da belki durum çok daha karmaşık. Kilit satın almanın sırası hakkında çok açık bir fikir edinmeniz yeterlidir. Tüm varsayımları inceleyin. Hiçbir şeye güvenme.
Bruce Ediger

+1 kodu dikkatlice okumanızı ve tüm kilitleme işlemlerini yalıtımlı olarak incelemenizi önerir. Tek bir düğümü dikkatlice inceleyerek karmaşık bir grafiğe bakmak çok daha kolay, her şeyi bir kerede görmek ve görmekten çok. Kaç kez sorunu sadece koda bakarak ve kafamda farklı senaryolar çalıştırarak buldum.
Newtopian,

11
  1. Diğerlerinin de söylediği gibi ... kayıt için faydalı bilgiler edinebilirseniz, ilk önce bunu yapmak en kolay şey olduğu için deneyin.

  2. İlgili kilitleri tanımlayın. Sonsuza kadar bekleyen süreleri beklemek için bekleyen tüm muteksleri / semaforları değiştirin ... 5 dakika kadar saçma sapan bir şey. Zaman aşımına uğradığında hatayı günlüğe kaydedin. Bu, sizi en azından meseleye dahil olan kilitlerden birine yönlendirecektir. Zamanlamanın değişkenliğine bağlı olarak, şansınızı azaltabilir ve birkaç turdan sonra her iki kilidi de bulabilirsiniz. Zamanlanmış bekleme başarısız olduktan sonra ilk etapta nasıl bulunduğunuzu tanımlamak için sözde yığın izini kaydetmek için işlev hatası kodunun / koşullarını kullanın. Bu, konuyla ilgili iş parçacığını tanımlamanıza yardımcı olacaktır.

  3. Deneyebileceğiniz başka bir şey de, mutex / semafor hizmetlerinin çevresine bir sarmalayıcı kitaplığı oluşturmaktır. Hangi iş parçacıklarının her bir muteks içerdiğini ve hangi iş parçacıklarının muteks üzerinde beklediklerini izleyin. İpliklerin ne kadar süredir bloke olduğunu kontrol eden bir monitör dizisi oluşturun. Makul bir süre boyunca tetikleyin ve takip ettiğiniz durum bilgisini boşaltın.

Bir noktada, eski düz kod incelemesi gerekli olacak.


6

İlk adım (Péter'in dediği gibi) günlüğe kaydetmektir. Benim deneyimlerime rağmen bu genellikle problemli. Ağır paralel işlemlerde bu genellikle mümkün değildir. Bir keresinde sinir ağına benzer bir şeyi ayıklamak zorunda kaldım, saniyede 100k düğüm işledim. Hata sadece birkaç saat sonra meydana geldi ve hatta tek bir çıktı satırı günler sürecek şekilde işleri yavaşlattı. Günlük kaydı yapmak mümkün ise, hangi bölümde olduğunu öğrenene kadar verilere daha az, programın akışına daha çok odaklanın. Her bir işlevin başında sadece basit bir satır ve doğru işlevi bulabilirseniz, bunu küçük parçalara bölün.

Başka bir seçenek, hatayı lokalize etmek için kodun ve verilerin bir kısmını kaldırmaktır. Belki sadece bazı dersleri alan ve sadece en temel testleri yapan küçük bir program bile yazabiliriz (yine de elbette birkaç konuda). Gerçek işlem durumu hakkında herhangi bir çıktı, örneğin GUI ile ilgili her şeyi kaldırın. (Kullanıcı arayüzünü hatanın kaynağı olarak yeterince sık buldum)

Kodunuzda kilidi başlatmak ve serbest bırakmak arasında tam bir mantıksal kontrol akışı izlemeye çalışın. Yaygın bir hata, bir işlevin başında kilitlemek, sonunda kilidini açmak, ancak arada bir yerde koşullu bir geri dönüş ifadesi olabilir. İstisnalar, serbest bırakmayı da önleyebilir.


"İstisnalar salıvermeyi engelleyebilir" -> Kapsamlı değişkenleri olmayan dillere acıyorum: /
Matthieu M.

1
@Matthieu: Değişkenlerin kapsamını almış ve aslında bunları doğru bir şekilde kullanmak iki farklı şey olabilir. Ayrıca, belirli bir dilden söz etmeden genel olarak olası sorunları istedi. Yani bu, kontrol akışını etkileyebilecek bir şeydir.
thorsten müller

3

En iyi arkadaşlarım kodun içindeki ilginç yerlere baskı / kayıt ifadeleridi. Bunlar genellikle uygulamada gerçekte neler olup bittiğini daha iyi anlamama yardımcı oluyor, bu da farklı iplikler arasındaki zamanlamayı bozmadan böceği yeniden üretmeyi engelliyor.

Bu başarısız olursa, geriye kalan tek yöntem koduma bakmak ve çeşitli iş parçacıklarının ve etkileşimlerin zihinsel bir modelini oluşturmaya çalışmaktır ve görünüşte olanları elde etmek için olası çılgın yollarını düşünmeye çalışmak :-) kendimi çok deneyimli bir kilitlenme avcısı olarak görüyorum. Umarım diğerleri de öğrenebileceğim daha iyi fikirler verebilir. :-)


1
Bugün bunun gibi birkaç ölü kilit hata ayıkladım. İşin püf noktası pthread_mutex_lock () işlevini, satır numarasını, dosya adını ve mutex değişkeninin adını (belirterek) kilidi aldıktan sonra basan bir makroya sarmaktı. Aynı şeyi pthread_mutex_unlock () için de yapın. İpimin donduğunu gördüğümde, son iki mesaja bakmak zorunda kaldım, kilitlemeye çalışan iki konu vardı ama asla bitiremedi! Şimdi kalan tek şey çalışma zamanında bunu değiştirmek için bir mekanizma eklemek. :-)
Plumenator

3

Her şeyden önce, bu kodun yazarını almaya çalışın. Muhtemelen ne yazdığı hakkında bir fikri olacak. İkiniz de sorunu sadece konuşarak saptayamazsanız bile, En azından çıkma bölümünü belirlemek için onunla oturup, onun kodunu yardım almadan anlamanızdan çok daha hızlı olacaktır.

Péter Török'in dediği gibi başarısız olmak, Logging'in muhtemelen yoludur. Bildiğim kadarıyla, Debugger çok iş parçacıklı ortamda kötü bir iş çıkardı. kilidin nerede olduğunu bulmaya çalışın, hangi kaynakların beklediğini ve yarış koşulunun ne durumda olduğunu bir bütün olarak alın.


hayır, günlüğe kaydetme, sizin düşmanınızdır - yavaş giriş yapmayı bıraktığınızda, programın davranışını, günlüğe kaydetme etkinken kusursuz çalışan bir program elde etmenin kolay olduğu noktaya, ancak günlüğe kaydetme kapatıldığında kilitlenmelere dönüştürürsünüz. Bir programı çok çekirdekli bir işlemciden ziyade tek bir bilgisayarda çalıştırırken karşılaşacağınız sorun aynı.
gbjbaanb

@gbjbaanb, bence senin düşmanın çok zor. Belki de ara sıra seni yarı yolda bırakan en iyi arkadaşın olduğunu söylemek doğru olur. Bu sayfada, günlüğe kaydetmenin iyi bir ilk adım olduğunu söyleyen diğer insanlar ile aynı fikirdeyim, kodun incelemesi başarısız olduktan sonra - çoğu zaman (aslında çoğu zaman benim deneyimime göre) basit bir kayıt stratejisi belirlenir Kolayca sorun ve bitti. Aksi taktirde, başka yöntemlere başvurmak gerekir, ancak iş için en iyi araç olanı denemekten kaçınmanın iyi bir tavsiye olduğunu sanmıyorum, çünkü her zaman yardımcı olmadığından.
Don Hatch

0

Bu soru beni cezbeder;) Her şeyden önce, konuyu sürekli olarak yeniden üretebildiğiniz için, kendinizi şanslı sayın. Her seferinde aynı istif ile aynı istisnayı alırsanız, bunun oldukça basit olması gerekir. Eğer değilse, o zaman istif aracına o kadar güvenmeyin, bunun yerine sadece genel nesnelere erişimi izleyin ve yürütme sırasındaki durum değişiklikleri.


0

Kilitlenmeleri hata ayıklamak zorunda kalırsanız, başınız zaten belada. Kural olarak, mümkün olan en kısa süre için kilit kullanın - mümkünse hiç kullanmayın. Kilitlenip önemsiz olmayan kodlara geçtiğiniz herhangi bir durumdan kaçınılmalıdır.

Elbette programlama ortamınıza bağlıdır, ancak bir kaynağa yalnızca tek bir iş parçasından erişmenize izin verebilecek sıralı sıralar gibi şeylere bakmalısınız.

Ve sonra eski ama yanılmaz bir strateji var: Her kilit için seviye 0'dan başlayarak bir "seviye" atayın. Seviye 0 kilidini alırsanız diğer kilitlere izin verilmez. Seviye 1 kilidi aldıktan sonra, seviye 0 kilidi alabilirsiniz. Seviye 10 kilidi aldıktan sonra, seviye 9 veya daha düşük seviyelerde kilitler alabilirsiniz.

Bunu yapmak imkansız bulursanız, kodunuzu düzeltmeniz gerekir, çünkü kilitlenmelere maruz kalırsınız.

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.