Bir Yöntemi İplik Güvenli Kılan Nedir? Kurallar neler?


156

Bir yöntemi iş parçacığı açısından güvenli kılan şeylerle ilgili genel kurallar / yönergeler var mı? Muhtemelen bir milyon bir defalık durum olduğunu anlıyorum, ama genel olarak ne olacak? Bu kadar basit mi?

  1. Bir yöntem yalnızca yerel değişkenlere erişiyorsa, iş parçacığı için güvenlidir.

Öyle mi? Bu statik yöntemler için de geçerli mi?

@Cybis tarafından sağlanan bir cevap:

Her iş parçacığı kendi yığını aldığından yerel değişkenler iş parçacıkları arasında paylaşılamaz.

Statik yöntemler için de durum böyle mi?

Bir yönteme bir referans nesne aktarılırsa, bu iplik güvenliğini bozar mı? Biraz araştırma yaptım ve bazı durumlar hakkında çok şey var, ancak bir yöntemin güvenli olduğundan emin olmak için takip etmek için sadece birkaç kural kullanarak yönergeleri tanımlayabilmeyi umuyordum.

Yani, sanırım nihai sorum: "İş parçacığı için güvenli bir yöntem tanımlayan kuralların kısa bir listesi var mı? Öyleyse, bunlar nedir?"

DÜZENLE
Burada birçok iyi noktaya değinildi. Bu sorunun asıl cevabı şöyledir: "İplik güvenliğini sağlamak için basit kurallar yok." Güzel. İnce. Ancak genel olarak kabul edilen cevabın kısa ve iyi bir özet oluşturduğunu düşünüyorum. Her zaman istisnalar vardır. Öyle olsun. Bununla yaşayabilirim.


59
Locketh olmadan diğer evreler tarafından da erişilebilen değişkenlere erişemezsiniz.
Hans Passant

4
Hanth pathant bir igor oldu!
Martin James

3
Ayrıca .. 'Bir locketh olmadan diğer iş parçacıkları tarafından da erişilebilen değişkenlere erişmeyeceksiniz' - okunan değer o zamanlar en son değilse veya aslında çok yanlışsa, o kadar önemli değildir.
Martin James

İşte Eric'in bir kasırgaya girmesi için güzel bir blog.
RBT

Yanıtlar:


140

Bir yöntem (örnek veya statik) yalnızca bu yöntemde kapsam dahilindeki değişkenlere başvuruyorsa, her iş parçacığının kendi yığını olduğu için iş parçacığı güvenlidir:

Bu durumda, birden fazla iş parçacığı ThreadSafeMethodaynı anda sorunsuz olarak çağırabilir .

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number; // each thread will have its own variable for number.
        number = parameter1.Length;
        return number;
    }
}

Yöntem, yalnızca yerel olarak kapsanan değişkenlere başvuran başka bir sınıf yöntemini çağırdığında da bu doğrudur:

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number;
        number = this.GetLength(parameter1);
        return number;
    }

    private int GetLength(string value)
    {
        int length = value.Length;
        return length;
    }
}

Bir yöntem (nesne durumu) özelliklerine veya alanlarına (örnek veya statik) erişirse, değerlerin farklı bir iş parçacığı tarafından değiştirilmediğinden emin olmak için kilitleri kullanmanız gerekir.

public class Thing
{
    private string someValue; // all threads will read and write to this same field value

    public int NonThreadSafeMethod(string parameter1)
    {
        this.someValue = parameter1;

        int number;

        // Since access to someValue is not synchronised by the class, a separate thread
        // could have changed its value between this thread setting its value at the start 
        // of the method and this line reading its value.
        number = this.someValue.Length;
        return number;
    }
}

Yönteme aktarılan veya yapılamayan tüm parametrelerin, yöntemin kapsamı dışındaki başka bir iş parçacığı tarafından değiştirilebileceğini bilmelisiniz.

Uygun eşzamanlılığı sağlamak için kilitleme kullanmanız gerekir.

daha fazla bilgi için bkz. lock deyimi C # başvuru ve ReadWriterLockSlim .

kilit çoğunlukla bir kerede bir işlevsellik sağlamak
ReadWriterLockSlimiçin yararlıdır , birden fazla okuyucuya ve tek yazarlara ihtiyacınız varsa yararlıdır.


15
Üçüncü örnekte private string someValue;değil static, her örneği olduğunu değişkenin ayrı bir kopyasını alacak böylece. Peki bunun nasıl iş parçacığı için güvenli olmadığını açıklayabilir misiniz?
Bharadwaj

29
ThingBirden fazla iş parçacığı tarafından erişilen bir sınıf örneği varsa
Bharadwaj

112

Bir yöntem yalnızca yerel değişkenlere erişiyorsa, iş parçacığı için güvenlidir. Öyle mi?

Kesinlikle hayır. Tek bir iş parçacığından erişilen tek bir yerel değişkeni içeren, ancak iş parçacığı için güvenli olmayan bir program yazabilirsiniz:

https://stackoverflow.com/a/8883117/88656

Bu statik yöntemler için de geçerli mi?

Kesinlikle hayır.

@Cybis tarafından sağlanan bir cevap şuydu: "Yerel değişkenler evreler arasında paylaşılamaz, çünkü her evre kendi yığınını alır."

Kesinlikle hayır. Bir yerel değişkenin ayırt edici özelliği, değişken havuzda tahsis edilmemesi değil , yalnızca yerel kapsamdan görülebilir olmasıdır . Aynı yasal değişkene iki farklı evreden erişmek tamamen yasaldır ve mümkündür. Bunu anonim yöntemler, lambdalar, yineleyici bloklar veya zaman uyumsuz yöntemler kullanarak yapabilirsiniz.

Statik yöntemler için de durum böyle mi?

Kesinlikle hayır.

Bir yönteme bir referans nesne aktarılırsa, bu iplik güvenliğini bozar mı?

Olabilir.

Biraz araştırma yaptım ve bazı durumlar hakkında çok şey var, ancak bir yöntemin güvenli olduğundan emin olmak için takip etmek için sadece birkaç kural kullanarak yönergeleri tanımlayabilmeyi umuyordum.

Hayal kırıklığı ile yaşamayı öğrenmek zorunda kalacaksınız. Bu çok zor bir konu.

Yani, sanırım nihai sorum: "İş parçacığı için güvenli bir yöntem tanımlayan kuralların kısa bir listesi var mı?

Hayır! Örneğimden daha önce gördüğünüz gibi , boş bir yöntem iş parçacığı için güvenli olmayabilir . Siz de sorabilirsiniz "bir yöntemin doğru olmasını sağlayan kuralların kısa bir listesi var mı " Hayır yok. İplik güvenliği son derece karmaşık bir tür doğruluktan başka bir şey değildir.

Ayrıca, soruyu sormanız, iplik güvenliği hakkındaki temel yanlış anlaşılmanızı gösterir. İş parçacığı güvenliği, bir programın yerel özelliği değil küreseldir . Doğru olmanın bu kadar zor olmasının nedeni , tüm programın iş parçacığı davranışı hakkında tam bir bilgiye sahip olmanızdır. , güvenliğini sağlamak .

Yine örneğime bakın: her yöntem önemsizdir . Yöntemlerin birbiriyle "global" düzeyde etkileşime girmesini sağlayarak programın kilitlenmesini sağlar. Her yönteme bakamaz ve "güvenli" olarak kontrol edemezsiniz ve daha sonra tüm programın güvenli olmasını bekleyebilirsiniz, sonuç olarak yapabileceğinizden daha fazlası, çünkü eviniz de evin% 100 oyuk olmayan tuğlalarından yapılmıştır olmayan içi boş. Bir evin içi boşluğu, parçalarının özelliklerinin bir toplamı değil, her şeyin küresel bir özelliğidir.


15
Temel açıklama: İş parçacığı güvenliği, bir programın yerel bir özelliği değil, küreseldir.
Greg D

5
@BobHorn: class C { public static Func<int> getter; public static Action<int> setter; public static void M() { int x = 0; getter = ()=>x; setter = y=>{x=y;};} } M () öğesini arayın, ardından iki farklı iş parçacığında C.getter ve C.setter'i arayın. Yerel değişken artık yerel olmasına rağmen iki farklı iş parçacığına yazılabilir ve bunlardan okunabilir. Yine: yerel bir değişkenin tanımlayıcı özelliği , yivin yığının üzerinde olması değil , lokal olmasıdır .
Eric Lippert

3
@BobHorn: Bence araçlarımızı otorite ve bilgi ile konuşabileceğimiz bir seviyede anlamakla daha fazla ilgisi var. Örneğin, asıl soru yerel bir değişkenin ne olduğu konusunda anlayış eksikliğine ihanet etmiştir. Bu hata oldukça yaygındır, ancak gerçek şu ki bir hatadır ve düzeltilmelidir. Yerel bir değişkenin tanımı oldukça hassastır ve buna göre ele alınmalıdır. :) Bu patolojik vakaların var olmadığını anlamayan "insanlar" için bir cevap değildir. Bunlar bir açıklamadır, böylece gerçekten ne sorduğunuzu anlayabilirsiniz. :)
Greg D

7
@EricLippert: Birçok sınıf için MSDN belgeleri 'Bu tür genel statik (Visual Basic'te Shared) üyeleri iş parçacığı güvenli olduğunu bildirir. Hiçbir örnek üyesinin iş parçacığı açısından güvenli olduğu garanti edilmez. ' veya bazen 'Bu tür iş parçacığı için güvenlidir'. (örn. String), iş parçacığı güvenliği küresel bir sorun olduğunda bu garantiler nasıl yapılabilir?
Mike Zboray

3
@mikez: Güzel soru. Ya bu yöntemler herhangi bir durumu mutasyona uğratmazlar veya yaptıkları zaman, durumu kilitsiz güvenli bir şekilde mutasyona uğratırlar veya kilit kullanırlarsa, bunu küresel "kilit sırasının" ihlal edilmeyeceğini garanti edecek şekilde yaparlar. Dizeler, yöntemleri hiçbir şeyi değiştirmeyen değişmez veri yapılarıdır, böylece dize kolayca güvenli hale getirilebilir.
Eric Lippert

12

Zor ve hızlı bir kural yoktur.

.NET'te kod dizisini güvenli hale getirmek için bazı kurallar ve bunların neden iyi kurallar olmadığı:

  1. İşlev ve çağırdığı tüm işlevler saf (yan etki yok) olmalı ve yerel değişkenler kullanmalıdır. Bu, kod iş parçacığı için güvenli hale getirecek olsa da, .NET'te bu kısıtlama ile yapabileceğiniz ilginç şeyler de çok az.
  2. Ortak bir nesne üzerinde çalışan her işlev lockortak bir şey üzerinde olmalıdır . Tüm kilitler aynı sırayla yapılmalıdır. Bu, kod iş parçacığını güvenli hale getirecektir, ancak inanılmaz derecede yavaş olacaktır ve birden fazla iş parçacığı kullanamayabilirsiniz.
  3. ...

Kod iş parçacığını güvenli kılan bir kural yoktur, yapabileceğiniz tek şey, etkin olarak kaç kez yürütüldüğüne bakılmaksızın kodunuzun çalışacağından emin olmaktır, her iş parçacığı her bir iş parçacığı ile herhangi bir noktada kesilebilir kendi durumu / konumu ve bu, ortak nesnelere erişen her işlev için (statik veya başka türlü).


10
Kilitlerin yavaş olması gerekmez. Kilitler inanılmaz derecede hızlı; tartışmasız bir kilit on ila yüz nanosaniye büyüklüğündedir . İtiraz edilen kilitler elbette keyfi olarak yavaştır; kilitlere itiraz ettiğiniz için yavaşladıysanız , çekişmeyi kaldırmak için programı yeniden arayın .
Eric Lippert

2
Sanırım # 2'yi yeterince açıklayamadım. Konu güvenli yapmak için bir yol olduğunu söylemeye çalışıyordum: herhangi bir ortak nesnelerin her erişim etrafında kilit koymak. Ve birden çok iş parçacığı olduğunu varsayarsak, çekişme yaratacaktır ve kilitler her şeyi yavaşlatacaktır. Kilitler eklenirken, sadece körü körüne kilitler eklenemez, çok iyi stratejik yerlere yerleştirilmelidirler.
earlNameless

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.