Neden pthread_cond_wait sahte uyandırma var?


145

Kılavuz sayfasını alıntılamak için:

Koşul değişkenlerini kullanırken, her koşulla ilişkili paylaşılan değişkenleri içeren her zaman bir Boole yüklemi, iş parçacığının devam etmesi durumunda doğru olur. Pthread_cond_timedwait () veya pthread_cond_wait () işlevlerinden sahte uyanmalar oluşabilir. Pthread_cond_timedwait () veya pthread_cond_wait () 'den dönüş bu yüklemin değeri hakkında hiçbir şey ifade etmediğinden, yüklem böyle bir dönüşte yeniden değerlendirilmelidir.

Yani, pthread_cond_waitsinyal vermemiş olsanız bile geri dönebilirsiniz. En azından ilk bakışta, bu oldukça iğrenç görünüyor. Yanlış bir değeri rastgele döndüren veya gerçekte uygun bir dönüş ifadesine ulaşmadan rastgele dönen bir işlev gibi olur. Büyük bir hata gibi görünüyor. Ancak bunu düzeltmek yerine man sayfasında belgelemeyi seçmiş olmaları, pthread_cond_waitsahte bir şekilde uyanmanın meşru bir nedeni olduğunu gösteriyor gibi görünüyor . Muhtemelen, nasıl çalıştığı konusunda yardımcı olabilecek bir şey vardır. Soru ne?

Neden yok pthread_cond_waitspuriously dönmek? Neden sadece uygun şekilde sinyal verildiğinde uyanacağını garanti edemiyor? Sahte davranışının nedenini açıklayan var mı?


5
Süreç bir sinyal yakaladığında geri dönmekle ilgili bir şey olduğunu hayal ediyorum. Çoğu * nix, bir sinyal kesildikten sonra engelleme çağrısını yeniden başlatmaz; sadece bir sinyal oluştuğunu söyleyen bir hata kodu ayarlar / döndürürler.
cHao

1
@cHao: yine de koşul değişkenlerinin sahte uyandırma için başka nedenleri olduğu için, bir sinyalin işlenmesi bir hata değildir pthread_cond_(timed)wait: "Bir sinyal iletilirse ... kesintiye uğramamış veya sahte uyandırma nedeniyle sıfıra dönecektir ". Diğer engelleme işlevleri, EINTRbir sinyal (örn. read) Tarafından kesildiğinde veya devam etmesi gerektiğinde (örn. pthread_mutex_lock) Gösterir . Dolayısıyla, sahte uyanmanın başka bir nedeni pthread_cond_waitolmasaydı, bunlardan biri gibi tanımlanabilirdi.
Steve Jessop

4
Wikipedia ile ilgili bir makale: Sahte uyandırma
Palec


Birçok işlev işlerini tam olarak yapamaz (kesintiye uğramış G / Ç) ve gözlemleme işlevleri, değişikliğin iptal edildiği veya geri döndürüldüğü bir dizinde değişiklik gibi olay olmayan bir olay alabilir. Sorun ne?
curiousguy

Yanıtlar:


77

David R. Butenhof tarafından "POSIX İş Parçacığıyla Programlama" (s. 80) bölümünde şu açıklama yer almaktadır :

Sahte uyandırma işlemleri kulağa garip gelebilir, ancak bazı çok işlemcili sistemlerde durum uyandırmayı tamamen öngörülebilir kılmak tüm koşul değişkeni işlemlerini önemli ölçüde yavaşlatabilir.

Aşağıdaki comp.programming.threads tartışmasında , tasarımın arkasındaki düşünceyi genişletiyor:

Patrick Doyle şunu yazdı: 
> Makalede Tom Payne şunu yazdı: 
Kaz Kylheku şunu yazdı: 
>>: Öyle çünkü uygulamalar bazen eklemekten kaçınamıyor 
>>: bu sahte uyanışlar; onları önlemek maliyetli olabilir.

Neden peki? Neden bu kadar zor? Örneğin,
>> bir sinyal geldiğinde bekleme zaman aşımına uğrayan durumlar? 

> Biliyor musun, pthreads tasarımcılarının böyle bir mantık kullanıp kullanmadığını merak ediyorum: 
> koşul değişkenlerinin kullanıcıları zaten çıkıştaki koşulu kontrol etmelidir, 
> bu yüzden izin verirsek onlara ek bir yük getirmeyeceğiz 
> sahte uyandırma; ve sahte izin vermek akla yatkın olduğu için
> uyandırma işlemleri bir uygulamayı daha hızlı yapabilir, yalnızca 
> izin verin. 

> Akıllarında belirli bir uygulamaları olmayabilir. 

Aslında çok da uzak değilsin, ama yeterince zorlamadın. 

Amaç yüklem döngüleri gerektirerek doğru / sağlam kodu zorlamaktı. Buydu
"temel tehditler" arasında makul derecede doğru akademik koşul tarafından yönlendirilen 
çalışma grubu, ancak kimsenin niyetle gerçekten aynı fikirde olmadığını düşünmüyorum 
bir kez ne anlama geldiğini anladılar. 

Bu niyeti birkaç gerekçe ile takip ettik. Birincisi
"dindar" bir döngü kullanarak uygulamayı kendi kusurlarına karşı korur 
kodlama uygulamaları. İkincisi, soyut olarak hayal etmek zor değildi
geliştirmek için bu gereksinimden yararlanabilecek makineler ve uygulama kodu 
optimize ederek ortalama durum bekleme işlemlerinin performansı 
senkronizasyon mekanizmaları. 
/ ------------------ [David.Buten ... @ compaq.com] ------------------ \ 
| Compaq Computer Corporation POSIX Konu Mimarı |
| Kitabım: http://www.awl.com/cseng/titles/0-201-63392-2/ |
\ ----- [http://home.earthlink.net/~anneart/family/dave.html] ----- / 


22
temelde bu hiçbir şey söylemiyor. Burada "işleri daha hızlı hale getirebilir" diye düşünen ilk açıklama dışında hiçbir açıklama yapılmaz, ancak hiç kimse nasıl yapılacağını ya da yapıp yapmadığını bilmez.
Bogdan Ionitza

107

'Sahte uyandırma' anlamına gelebilecek en az iki şey vardır:

  • Engellenen bir iş parçacığı , koşulda veya koşulda pthread_cond_waithiçbir arama yapılmamasına rağmen aramadan geri dönebilir .pthread_call_signalpthread_cond_broadcast
  • Bir pthread_cond_waitçağrı nedeniyle pthread_cond_signalveya nedeniyle çağrıda engellenen bir iş parçacığı pthread_cond_broadcast, ancak muteksi yeniden çağırdıktan sonra altta yatan yüklemin artık doğru olmadığı bulunmuştur.

Ancak ikinci durum, koşul değişkeni uygulaması önceki duruma izin vermese bile oluşabilir. Bir üretici tüketici kuyruğunu ve üç iş parçacığını düşünün.

  • İş parçacığı 1 bir öğeyi ayıkladı ve muteksi serbest bıraktı ve kuyruk artık boş. İş parçacığı, bazı CPU'da elde ettiği öğe ile ne yapıyorsa onu yapıyor.
  • İş parçacığı 2 bir öğeyi ayıklamaya çalışır, ancak pthread_cond_waitsinyal / yayın bekleyen çağrıdaki muteks, çağrılar ve bloklar altında işaretlendiğinde kuyruğun boş olduğunu bulur .
  • İş parçacığı 3 muteksi elde eder, kuyruğa yeni bir öğe ekler, koşul değişkenini bildirir ve kilidi serbest bırakır.
  • İş parçacığı 3'ten gelen bildirime yanıt olarak, koşulda bekleyen iş parçacığı 2'nin çalışması planlanır.
  • Ancak, iş parçacığı 2 CPU'ya girip kuyruk kilidini yakalamadan önce, iş parçacığı 1 geçerli görevini tamamlar ve daha fazla çalışma için sıraya geri döner. Kuyruk kilidini alır, yüklemi kontrol eder ve kuyrukta iş olduğunu bulur. İplik 3'ün yerleştirildiği öğeyi kaldırmaya devam eder, kilidi serbest bırakır ve iplik 3'ün sıkıldığı öğeyle ne yaparsa yapın.
  • İş parçacığı 2 şimdi bir CPU alır ve kilidi alır, ancak yüklemi kontrol ettiğinde kuyruğun boş olduğunu bulur. İş parçacığı 1 öğeyi 'çaldı', böylece uyandırma sahte görünüyor. 2. iş parçacığının durumu tekrar beklemesi gerekiyor.

Bu nedenle, yüklemi her zaman bir döngü altında kontrol etmeniz gerektiğinden, altta yatan koşul değişkenlerinin başka türden sahte uyandırmalara sahip olması fark etmez.


23
Evet. Esasen, bu, bir sayı ile senkronizasyon mekanizması yerine bir olay kullanıldığında gerçekleşir. Ne yazık ki, POSIX semaforlarının (yine de Linux'ta) spurius uyandırmalarına da maruz kaldığı anlaşılıyor. Senkronizasyon ilkellerinin temel bir işlevsellik arızasının sadece 'normal' olarak kabul edilmesi ve kullanıcı düzeyinde çözülmesi gerektiğini biraz garip buluyorum :( Muhtemelen, bir sistem çağrısı belgelenirse geliştiriciler çalışmaya devam edecek 'Sahte segfault' bölümü veya belki de 'Sahte URL'ye sahte bağlantı' veya 'Yanlış dosyanın sahte açılması'
Martin James

2
"Sahte uyandırma" nın daha yaygın senaryosu büyük olasılıkla pthread_cond_broadcast () çağrısının yan etkisidir. Diyelim ki 5 yayınlık bir havuzunuz var, ikisi yayına uyanıp işi yapıyor. Diğer üçü uyanır ve işin bittiğini bulur. Çok işlemcili sistemler, koşullu bir sinyalin kazara birden fazla iş parçacığını uyandırmasına da neden olabilir. Kod yüklemi tekrar kontrol eder, geçersiz bir durum görür ve uyku moduna geri döner. Her iki durumda da yüklemin kontrol edilmesi sorunu çözer. IMO, genel olarak, kullanıcılar ham POSIX mutekslerini ve koşullarını kullanmamalıdır.
CubicleSoft

1
@MartinJames - Klasik "sahte" EINTR nasıl olur? Bir döngüde EINTR için sürekli test yapmanın biraz can sıkıcı olduğunu ve kodu oldukça çirkin hale getirdiğini kabul edeceğim, ancak geliştiriciler rastgele kırılmaları önlemek için yine de yapıyorlar.
CubicleSoft

2
@Yola Hayır bunu yapamazsınız, çünkü pthread_cond_signal/broadcastmuteksi arayarak kilitlemeniz gerekiyor ve muteks arayarak kilidi açılana kadar bunu yapamayacaksınız pthread_cond_wait.
a3f

1
Bu yanıtın örneği çok gerçekçi ve tahminleri kontrol etmenin iyi bir fikir olduğunu kabul ediyorum. Ancak, "iş parçacığı 1 geçerli görevini tamamlar ve daha fazla iş için kuyruğa geri döner" ve "iş parçacığı 1" ile değiştirerek sorunlu adım atarak eşit derecede sağlam bir şekilde düzeltilemezdi ve beklemeye geri dönüyor koşul değişkeni "? Bu, yanıtta açıklanan hata modunu ortadan kaldıracak ve sahte uyandırmaların yokluğunda kodu doğru yapacağından eminim . Uygulamada sahte uyanmalar üreten gerçek bir uygulama var mı?
Quuxplusone

7

Bölüm "Durum Sinyal tarafından Birden Passionfish" pthread_cond_signal sahte wakekups içerir pthread_cond_wait ve pthread_cond_signal bir örnek uygulaması vardır.


2
Bence bu cevap yanlış. Bu sayfadaki örnek uygulama "herkese bildir" ile eşdeğer bir "bildir" uygulamasına sahiptir; ama aslında sahte uyandırmalar üretmiyor gibi görünüyor . Bir iş parçacığının uyanmasının tek yolu, "tümünü bildir" i çağıran başka bir iş parçacığı veya "-be-is-gerçekten-" her şeyi bildir "ibaresi-etiketli-" bir uyar "ı çağıran başka bir iş parçacığıdır.
Quuxplusone

5

Tasarım sırasında dikkate alınmadığını düşünmeme rağmen, gerçek bir teknik neden: İplik iptali ile birlikte, "sahte olarak" uyanma seçeneğinin alınmasının kesinlikle gerekli olabileceği koşullar vardır. Ne tür uygulama stratejilerinin mümkün olduğuna dair çok güçlü kısıtlamalar uygulamaya istekliyiz.

Kilit problem, eğer bir evre bloke olmuşken iptal durumunda ise pthread_cond_wait, yan etkiler koşul değişkeni üzerinde herhangi bir sinyal tüketmemiş gibi olmalıdır. Bununla birlikte, iptal işlemine başladığınızda zaten bir sinyal tüketmediğinizden emin olmak zordur (ve oldukça kısıtlayıcıdır) ve bu aşamada sinyali koşul değişkenine "yeniden göndermek" mümkün olmayabilir, çünkü Arayanın pthread_cond_signalkovarları yok ettiği ve içinde bulunduğu hafızayı serbest bıraktığı gerekçesiyle haklı olduğu bir durumda olmak.

Sahte uyandırma ödeneği size kolaylık sağlar. Bir koşul değişkeninde engellendiğinde iptal işlemine devam etmek yerine, zaten bir sinyal tüketmişseniz (veya ne olursa olsun tembel olmak istiyorsanız) bunun yerine sahte bir uyandırma bildirebilirsiniz, ve başarı ile geri dönün. Bu, iptal işlemine hiçbir şekilde müdahale etmez, çünkü doğru bir arayan bir dahaki sefere dönüp pthread_cond_waittekrar çağırdığında bekleyen iptal üzerinde etkili olur .

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.