Soru Threadsafe vs yeniden girişken


Son zamanlarda, başlık olarak bir soru sordum "Malloc ipliği güvenli mi?"Ve içimde sordum ki, "Malloc yeniden giriyor mu?"

Yeniden giren herkesin güvenli olduğu izleniminin altındaydım.

Bu varsayım yanlış mı?


76
2018-05-13 08:43


Menşei




Cevaplar:


Yeniden giriş fonksiyonları, C kütüphanesi başlıklarında karşılaşılan global değişkenlere dayanmaz. Strtok () vs strtok_r () örneğinde C.

Bazı işlevler, 'devam eden bir çalışma' saklamak için bir yere ihtiyaç duyar, yeniden giriş fonksiyonları, bu göstericiyi iş parçacığının kendi depolama alanı içinde global olarak değil, belirtmenize izin verir. Bu depolama arama işlevine özel olduğundan, kesintiye uğrayabilir ve Yeniden girilen (yeniden giriş) ve çoğu durumda, bu fonksiyonun çalışması için fonksiyonun yerine getirilmesinin ötesinde bir karşılıklı dışlama yapılması, genellikle iş parçacığı güvenli. Bununla birlikte, bu tanımla garanti edilmez.

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

Kısaca, reentrant sık sık iş parçacığı güvenli anlamına gelir ("iş parçacığı kullanıyorsanız, bu işlevin reentrant sürümünü kullanın"), ancak iş parçacığı güvenli her zaman yeniden giriş (veya ters) anlamına gelmez. İplik güvenliğini ararken eşzamanlılık Düşünmen gereken şey. Bir işlevi kullanmak için bir kilitleme ve karşılıklı dışlama aracı sağlamanız gerekiyorsa, işlev doğal olarak iş parçacığı güvenli değildir.

Ancak, tüm fonksiyonların da incelenmesi gerekmemektedir. malloc() reentrant olmaya gerek yoktur, herhangi bir iş parçacığı için giriş noktasının kapsamı dışında herhangi bir şeye bağlı değildir (ve kendisi iş parçacığı güvenlidir).

Statik olarak ayrılmış değerler döndüren işlevler değil mutex, futex veya başka bir atomik kilitleme mekanizması kullanılmadan emniyete alın. Yine de, kesintiye uğramayacaklarsa, reentran olmaya ihtiyaçları yoktur.

yani .:

static char *foo(unsigned int flags)
{
  static char ret[2] = { 0 };

  if (flags & FOO_BAR)
    ret[0] = 'c';
  else if (flags & BAR_FOO)
    ret[0] = 'd';
  else
    ret[0] = 'e';

  ret[1] = 'A';

  return ret;
}

Gördüğünüz gibi, birden fazla iş parçacığına sahip olmak, bir çeşit kilitleme olmaksızın bir felaket olur. Fakat yeniden giriş yapmak için hiçbir amacı yoktur. Dinamik olarak ayrılan bellek bazı gömülü platformlarda tabu olduğunda bu duruma geçersiniz.

Tamamen işlevsel programlamada, genellikle reentrant değil iş parçacığı güvenliğini ima eder, işlev giriş noktasına, özyinelemesine vb. atanan tanımlı veya anonim işlevlerin davranışına bağlı olur.

'İplik güvenli' koymak için daha iyi bir yoldur eşzamanlı erişim için güvenli İhtiyacı daha iyi gösteren


38
2018-05-13 08:55



Reentrant iş parçacığı güvenliğini ima etmez. Saf işlevler iplik güvenliğini ima eder. - Julio Guerra
Harika cevap Tim. Sadece açıklığa kavuşturmak için, “sık sık” dan yaptığım anlayış, iş parçacığı güvenliğinin, reentrant anlamına gelmediği, ancak aynı zamanda reentranın da diş güvenli olduğu anlamına gelmediği. Bir reentrant fonksiyonunun bir örneğini bulabilir misiniz değil evreli? - Riccardo
@ Tim Post "Kısacası, reentran genellikle iş parçacığı güvenliğini ifade eder (" iş parçacığı kullanıyorsanız bu işlevin reentrant sürümünü kullan "gibi), ancak iş parçacığı güvenliğini her zaman yeniden giriş anlamına gelmez." qt diyor tersi: "Bu nedenle, bir iş parçacığı güvenli işlevi her zaman reentrant, ancak bir reentrant işlevi her zaman iş parçacığı güvenli değildir." - 4pie0
ve wikipedia diyor Yine de başka bir şey: "Bu yeniden tanımlama kavramı, çok iş parçacıklı ortamlardaki iş parçacığı güvenliğinden farklıdır. Yeniden bağlanan bir alt yordam, iş parçacığı güvenliğini sağlayabilir. [1] ancak tek başına reentran olmak, her durumda iş parçacığı için güvenli olmayabilir. Tersine, iş parçacığı için güvenli kodun mutlaka reentrant olması gerekmez (...) " - 4pie0
@Riccardo: Sinyali değişkenlerle senkronize edilen ancak sinyal / kesinti işleyicileriyle kullanım için tam bellek bariyeri olmayan işlevler genellikle yeniden girer ancak iş parçacığı güvenlidir. - doynax


Tanıma göre değişir. Örneğin Qt kullanır aşağıdaki:

  • Bir iş parçacığı güvenli * işlevi, birden çok iş parçacığı eşzamanlı olarak çağrılabilir, hatta çağrılar paylaşılan verileri kullansa bile, paylaşılan verilere yapılan tüm başvurular serileştirilir.

  • bir evresel işlev aynı anda birden fazla ileti dizisinden de çağrılabilir, ancak yalnızca her bir çağrı kendi verilerini kullanıyorsa.

Böylece, bir evreli Fonksiyon her zaman reentrant, ama evresel Fonksiyon her zaman iplik güvenli değildir.

Ek olarak, bir sınıfın olduğu söylenir. evresel eğer her iş parçacığı, sınıfın farklı bir örneğini kullandığı sürece, üye işlevleri birden çok iş parçacığından güvenle çağrılabilir. Sınıf evreli eğer tüm iş parçacıkları sınıfın aynı örneğini kullanıyor olsa bile, üye işlevleri birden çok iş parçacığı arasından güvenli bir şekilde çağrılabilir.

ama aynı zamanda dikkatlilar:

Not: Multithreading alanındaki terminoloji tamamen standart değildir. POSIX, C API'leri için biraz farklı olan reentrant ve thread-safe tanımlarını kullanır. Diğer nesne yönelimli C ++ sınıf kitaplıklarını Qt ile kullanırken, tanımların anlaşıldığından emin olun.


54
2018-05-13 09:08



Bu hüküm veren tanımı çok güçlüdür. - qweruiop
Downvote. Bir iş parçacığı güvenli işlevi her zaman KIRMIZI DEĞİLDİR. - SandBag_1996
Global / statik varyasyon kullanmıyorsa, bir işlev hem reentrant hem de thread-safe'dir. Konu - güvenli: Birçok iş parçacığı aynı anda işlevini çalıştığında, herhangi bir ırk var mı? Global var kullanıyorsanız, korumak için kilidi kullanın. bu yüzden iplik güvenlidir. reentrant: Eğer bir fonksiyonun çalışması sırasında bir sinyal oluşursa ve fonksiyonunuzu tekrar sinyal olarak çağırırsanız, bu güvenli midir? Böyle bir durumda, çoklu iş parçacığı yoktur. En iyisi, onu normalleştirmek için herhangi bir statik / global var kullanmamanız veya örnek 3'teki gibi. - keniee van


TL; DR: Bir işlev hem reentrant hem de thread-safe olabilir.

Wikipedia makaleleri parçacığı emniyet ve Evreselliğin okumaya değer. İşte birkaç alıntı:

Bir işlev evreli Eğer:

yalnızca paylaşılan veri yapılarını yönetir   Birden çok tarafından güvenli yürütmeyi garanti eden bir yöntem   iş parçacığı aynı anda.

Bir işlev evresel Eğer:

yürütülmesi sırasında herhangi bir noktada kesintiye uğrayabilir   ve daha sonra güvenli bir şekilde tekrar ("yeniden girildi") önce   önceki çağrılar tam yürütme.

Olası bir tekrarlama örneği olarak Vikipedi, sistem kesintileri ile çağrılmak üzere tasarlanan bir işlev örneğini verir: başka bir kesinti olduğunda zaten çalışıyor olduğunu varsayalım. Ancak, sistem kesintileriyle kodlamadığınız için güvendiğinizi düşünmeyin: Geri aramalar veya yinelemeli işlevler kullanıyorsanız, tek iş parçacıklı programda yeniden ilişkilendirme sorunlarınız olabilir.

Karışıklık önlemek için anahtar reentrantı ifade eder   sadece bir iş parçacığı yürütüyor. Zaman zaman bir kavramdır   çoklu görevli işletim sistemi yoktu.

Örnekler

(Wikipedia makalelerinden biraz değiştirildi)

Örnek 1: thread-safe değil, reentrant değil

/* As this function uses a non-const global variable without
   any precaution, it is neither reentrant nor thread-safe. */

int t;

void swap(int *x, int *y)
{
    t = *x;
    *x = *y;
    *y = t;
}

Örnek 2: thread-safe, reentrant değil

/* We use a thread local variable: the function is now
   thread-safe but still not reentrant (within the
   same thread). */

__thread int t;

void swap(int *x, int *y)
{
    t = *x;
    *x = *y;
    *y = t;
}

Örnek 3: thread-safe değil, reentrant

/* We save the global state in a local variable and we restore
   it at the end of the function.  The function is now reentrant
   but it is not thread safe. */

int t;

void swap(int *x, int *y)
{
    int s;
    s = t;
    t = *x;
    *x = *y;
    *y = t;
    t = s;
}

Örnek 4: iplik-güvenli, reentrant

/* We use a local variable: the function is now
   thread-safe and reentrant, we have ascended to
   higher plane of existence.  */

void swap(int *x, int *y)
{
    int t;
    t = *x;
    *x = *y;
    *y = t;
}

53
2017-10-30 22:34



Ben sadece teşekkür etmek için yorum yapmak zorunda değilim biliyorum, ama bu yeniden giriş ve iş parçacığı güvenli işlevleri arasındaki farkları ortaya koyan en iyi resimlerden biridir. Özellikle çok net ve net terimler kullandınız ve 4 kategori arasında ayrım yapmak için mükemmel bir örnek işlev seçtiniz. Yani, teşekkürler! - ryyker
Global / statik varyasyon kullanmıyorsa, bir işlev hem reentrant hem de thread-safe'dir. Konu - güvenli: Birçok iş parçacığı aynı anda işlevini çalıştığında, herhangi bir ırk var mı? Global var kullanıyorsanız, korumak için kilidi kullanın. bu yüzden iplik güvenlidir. reentrant: Eğer bir fonksiyonun çalışması sırasında bir sinyal oluşursa ve fonksiyonunuzu tekrar sinyal olarak çağırırsanız, bu güvenli midir? Böyle bir durumda, çoklu iş parçacığı yoktur. onu reentrant yapmak için herhangi bir statik / global var kullanamazsınız ... - keniee van
Görünüşe göre, 3 numaralı durum, reentran değil: eğer bir sinyal işleyici t = *x, çağrılar swap(), sonra t beklenmedik sonuçlara yol açacak şekilde geçersiz kılınacak. - rom1v