Threadsafe vs yeniden giren


Yanıtlar:


42

Yeniden giriş fonksiyonları, C kütüphanesi başlıklarında gösterilen global değişkenlere dayanmaz .. strtok () - strtok_r (), örneğin C.

Bazı işlevler 'devam eden çalışmayı' depolamak için bir yere ihtiyaç duyar, yeniden giriş işlevleri bu işaretçiyi iş parçacığının kendi deposunda tanımlamanıza izin verir, genel olarak değil. Bu depolama, arama işlevine özel olduğundan, kesintiye uğrayabilir ve yeniden girilebilir (yeniden giriş) ve çoğu durumda işlevin bunun çalışması için uyguladığının ötesinde karşılıklı dışlama gerekmediğinden, bunlar genellikle güvenli iş parçacığı . Ancak bu, tanım gereği garanti edilmez.

Errno, POSIX sistemlerinde biraz farklı bir durumdur (ve tüm bunların nasıl çalıştığına dair herhangi bir açıklamada tuhaf olma eğilimindedir) :)

Kısacası, evresel, genellikle iş parçacığı güvenli anlamına gelir ("iş parçacığı kullanıyorsanız bu işlevin evresel sürümünü kullanın" gibi), ancak iş parçacığı güvenliği her zaman yeniden giren (veya tersi) anlamına gelmez. İş parçacığı güvenliğine bakarken, eşzamanlılık düşünmeniz gereken şeydir. Bir işlevi kullanmak için bir kilitleme ve karşılıklı dışlama aracı sağlamanız gerekiyorsa, işlev doğası gereği iş parçacığı açısından güvenli değildir.

Ancak, tüm işlevlerin her ikisi için de incelenmesi gerekmez. malloc()evresel olmaya gerek yoktur, herhangi bir iş parçacığı için giriş noktasının kapsamı dışındaki herhangi bir şeye bağlı değildir (ve kendisi iş parçacığı güvenlidir).

Statik olarak ayrılmış değerleri döndüren işlevler , bir muteks, futex veya başka atomik kilitleme mekanizması kullanılmadan iş parçacığı açısından güvenli değildir . Yine de, kesintiye uğramayacaklarsa tekrar giriş yapmaları gerekmez.

yani:

Gördüğünüz gibi, birden fazla iş parçacığına sahip olmak, bir çeşit kilitleme olmadan bunu kullanmak bir felaket olur .. ama yeniden girme amacı yoktur. Bazı gömülü platformlarda dinamik olarak ayrılmış bellek tabu olduğunda bununla karşılaşacaksınız.

Tamamen işlevsel Programlamada, genellikle için evresel gelmez parçacığı güvenli ima, bu fonksiyon giriş noktasına geçirilen tanımlanmış veya anonim işlevlerin, özyinelemenin vb davranışına bağlı olacağını

Eşzamanlı erişim için 'iş parçacığı güvenli' koymanın daha iyi bir yolu güvenlidir , bu da ihtiyacı daha iyi gösterir.


2
Yeniden giriş, iş parçacığı güvenli olduğu anlamına gelmez. Saf işlevler, iş parçacığı güvenliğini ifade eder.
Julio Guerra

Harika cevap Tim. Sadece açıklığa kavuşturmak için, "sık sık" nızdan anladığım kadarıyla, iş parçacığı güvenli evresel anlamına gelmez, aynı zamanda evreli iş parçacığı güvenli anlamına gelmez. İş parçacığı açısından güvenli olmayan bir evresel işlevin bir örneğini bulabilir misiniz ?
Riccardo

@ Tim Post "Kısacası, evresel genellikle iş parçacığı güvenli anlamına gelir (" iş parçacığı kullanıyorsanız bu işlevin evresel sürümünü kullanın "gibi), ancak iş parçacığı güvenliği her zaman yeniden giren anlamına gelmez." qt tersini söyler : "Bu nedenle, iş parçacığı güvenli işlev her zaman evreseldir, ancak evresel işlev her zaman iş parçacığı açısından güvenli değildir."
4pie 0

ve wikipedia başka bir şey daha söylüyor : "Bu yeniden giriş tanımı, çok iş parçacıklı ortamlardaki iş parçacığı güvenliğinden farklıdır. Bir yeniden giriş alt yordamı iş parçacığı güvenliğini sağlayabilir, [1] ancak tek başına evresel olmak iş parçacığı güvenli olmak için yeterli olmayabilir Tüm durumlarda. Tersine, iş parçacığı güvenli kodun tekrar girişli olması gerekmez (...) "
4pie0

@Riccardo: Uçucu değişkenler aracılığıyla senkronize edilen ancak sinyal / kesme işleyicileriyle kullanım için tam bellek engelleri olmayan işlevler genellikle yeniden girişlidir, ancak iş parçacığı güvenlidir.
doynax

79

TL; DR: Bir işlev evresel, iş parçacığı güvenli olabilir veya ikisi birden olabilir.

İplik güvenliği ve yeniden giriş için Wikipedia makaleleri okumaya değer. İşte birkaç alıntı:

Bir işlev iş parçacığı açısından güvenlidir :

yalnızca paylaşılan veri yapılarını, aynı anda birden çok iş parçacığı tarafından güvenli yürütmeyi garanti edecek şekilde işler.

Bir işlev evreseldir :

yürütme sırasında herhangi bir noktada kesilebilir ve daha önceki çağrılarının yürütülmesi tamamlanmadan önce güvenli bir şekilde yeniden çağrılabilir ("yeniden girilmiş").

Olası yeniden giriş örnekleri olarak Wikipedia, sistem kesintileri tarafından çağrılmak üzere tasarlanmış bir işlev örneğini verir: başka bir kesinti olduğunda zaten çalışmakta olduğunu varsayalım. Ancak, sırf sistem kesintileriyle kod yazmadığınız için güvende olduğunuzu düşünmeyin: Geri çağırma veya özyinelemeli işlevler kullanırsanız, tek iş parçacıklı bir programda yeniden giriş sorunları yaşayabilirsiniz.

Karışıklıktan kaçınmanın anahtarı, evrenin yalnızca bir iş parçacığının yürütülmesini ifade etmesidir. Çok görevli işletim sistemlerinin bulunmadığı zamandan kalma bir kavramdır.

Örnekler

(Wikipedia makalelerinden biraz değiştirilmiştir)

Örnek 1: iş parçacığı güvenli değil, evresel değil

Örnek 2: iş parçacığı güvenli, evresel değil

Örnek 3: iş parçacığı güvenli değil, evresel

Örnek 4: iş parçacığı güvenli, evresel


10
Sadece teşekkür etmek için yorum yapmamam gerektiğini biliyorum, ancak bu, yeniden giriş ve iş parçacığı güvenli işlevleri arasındaki farkları ortaya koyan en iyi örneklerden biridir. Özellikle, çok kısa ve net terimler kullandınız ve 4 kategori arasında ayrım yapmak için harika bir örnek işlevi seçtiniz. Yani, teşekkürler!
ryyker

11
Bir sinyal işleyici, sonra kesintiye eğer: geçtim exemple 3 evresel değildir gibi geliyor bana t = *x, çağrılar swap(), daha sonra tbeklenmedik sonuçlara yol açan, geçersiz kılınan olacaktır.
rom1v

1
@ SandBag_1996, swap(5, 6)bir swap(1, 2). Sonra t=*x, s=t_originalve t=5. Şimdi, kesintiden sonra s=5ve t=1. Ancak, ikinci swapdönüşlerden önce, içeriği geri yükleyecek t=s=5. Şimdi, ilk geri dönmek swapile t=5 and s=t_originalve sonra da devam t=*x. Dolayısıyla, işlev yeniden giren gibi görünüyor. Her aramanın kendi syığınında ayrılmış bir kopyasını aldığını unutmayın .
urnonav

4
@ SandBag_1996 Varsayım, eğer fonksiyon kesintiye uğrarsa (herhangi bir noktada), sadece tekrar çağrılacak ve orijinal çağrıya devam etmeden önce tamamlanana kadar bekleyeceğiz. Başka bir şey olursa, temelde çok iş parçacıklıdır ve bu işlev iş parçacığı için güvenli değildir . Fonksiyonun ABCD yaptığını varsayalım, biz sadece AB_ABCD_CD veya A_ABCD_BCD veya hatta A__AB_ABCD_CD__BCD gibi şeyleri kabul ediyoruz. Kontrol edebileceğiniz gibi, örnek 3 bu varsayımlar altında iyi çalışacaktır, bu yüzden evreseldir. Bu yardımcı olur umarım.
MiniQuark

1
@ SandBag_1996, muteks aslında onu evresel olmayan yapar. İlk çağrı muteksi kilitler. İkinci çağrı gelir - çıkmaz.
urnonav

56

Tanıma bağlıdır. Örneğin Qt aşağıdakileri kullanır :

  • İş parçacığı güvenli * bir işlev, çağrılar paylaşılan verileri kullandığında bile birden çok iş parçacığından aynı anda çağrılabilir çünkü paylaşılan verilere yönelik tüm referanslar serileştirilir.

  • Bir evresel işlev birden çok iş parçacığından aynı anda çağrılabilir, ancak yalnızca her çağrı kendi verilerini kullanıyorsa.

Bu nedenle, iş parçacığı güvenli bir işlev her zaman evreseldir, ancak bir evresel işlev her zaman iş parçacığı için güvenli değildir.

Uzantı olarak, bir sınıfın, her evre farklı bir sınıf örneği kullandığı sürece, üye işlevleri birden çok evreden güvenli bir şekilde çağrılabiliyorsa , sınıfın evresel olduğu söylenir . Tüm evreler aynı sınıf örneğini kullansa bile, üye işlevleri birden çok evreden güvenle çağrılabiliyorsa , sınıf iş parçacığı açısından güvenlidir .

ama aynı zamanda uyarıyorlar:

Not: Çoklu okuma alanındaki terminoloji tamamen standartlaştırılmamıştır. POSIX, C API'leri için biraz farklı olan evresel ve iş parçacığı güvenli tanımlarını kullanır. Qt ile diğer nesne yönelimli C ++ sınıf kitaplıklarını kullanırken, tanımların anlaşıldığından emin olun.


2
Bu evresel tanım çok güçlü.
qweruiop

Herhangi bir global / statik değişken kullanmıyorsa, bir işlev hem evresel hem de iş parçacığı açısından güvenlidir. İş parçacığı güvenli: birçok iş parçacığı aynı anda işlevinizi çalıştırdığında, herhangi bir yarış var mı ?? Global var kullanıyorsanız, korumak için kilidi kullanın. bu yüzden iş parçacığı açısından güvenlidir. reentrant: işlevin yürütülmesi sırasında bir sinyal oluşursa ve sinyalde işlevinizi tekrar çağırırsanız, güvenli midir ??? böyle bir durumda, birden fazla iş parçacığı yoktur. En iyisi, onu reentrant yapmak için herhangi bir statik / global var kullanmamanız veya örnek 3'teki gibi.
keniee van
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.