C #: İş parçacığı güvenli ve atom arasındaki fark nedir?


cevap 1:

İş parçacığı için güvenli araçlar, birden fazla iş parçacığından erişildiğinde dağılmaz; atomik, bölünemez anlamına gelir, bu bağlamda kesintisiz.

Kilitleri uygulamak için iki seçeneğiniz vardır:

  1. Atomik işlemler için donanım desteğine sahip olun - Test-and-set gibi bir bütün olarak yürütülen özel kompozit talimatlar.Akıllı olun (ve sonuçlara maruz kalın) - Peterson algoritması.

Ayrıntılardaki örneğinizde, her ikisi de güvensizdir; doğru anladıysam, şöyle bir şey demek istedin:

genel sınıf Güvenli değil
{
    özel nesne ulock = yeni nesne ();

    public int Güvensiz1 {get; Ayarlamak; } = 0;

    özel int _unsafe2 = 0;
    kamu int Güvenli olmayan2
    {
        almak
        {
            kilitle (ulock)
            {
                dönüş _unsafe2;
            }
        }

        Ayarlamak
        {
            kilitle (ulock)
            {
                _unsafe2 = değer;
            }
        }
    }
}

Test kodu:

var u = yeni Güvensiz ();

Paralel. (0, 10000000, _ => {u.Unsafe1 ++;});
Paralel. (0, 10000000, _ => {u.Unsafe2 ++;});

Console.WriteLine (string.Format ("{0} - {1}", u.Unsafe1, u.Unsafe2));

Sonuç (mümkün olanlardan biri):

4648265 - 4149827

Her ikisi için de güncellemelerin yarısından fazlası kayboldu.

Nedeni ++'ın atomik olmaması - aslında üç ayrı işlemdir:

  1. Değeri al. Değere 1 ekleyin.

Atomik bir artış işlemi sağlayarak bunu düzeltebiliriz - bunu yapmanın birçok yolu vardır, ancak işte iki:

genel sınıf Güvenli
{
    özel nesne slock = yeni nesne ();

    public int Safe1 {get; Ayarlamak; }
    public void SafeIncrement1 ()
    {
        kilitle (ulock)
        {
            this.Safe1 ++;
        }
    }

    özel int _safe2 = 0;
    public int Güvenli2
    {
        almak
        {
            dönüş _safe2;
        }
        Ayarlamak
        {
            _safe2 = değer;
        }
    }
    public void SafeIncrement2 ()
    {
        Kilitli.Kart (ref _safe2);
    }
}

Test kodu:

var s = yeni Kasa ();

Paralel. (0, 10000000, _ => {s.SafeIncrement1 ();});
Paralel. (0, 10000000, _ => {s.SafeIncrement2 ();});

Console.WriteLine (string.Format ("{0} - {1}", s.Safe1, s.Safe2));

Her iki durumda da sonuçlar doğrudur. Birincisi tüm kompozit ++ işleminin etrafına bir kilit koyarken, ikincisi atomik işlemler için donanım desteğini kullanır.

Interlocked.Increment ile yukarıdaki ikinci varyantı not edin, çok daha hızlıdır, ancak aslında daha düşük seviyededir ve kutunun dışında neler yapabileceğiyle sınırlıdır; ancak, Kilitli paketteki işlemler aşağıdakileri uygulamak için kullanılabilir:

  1. “Kötümser eşzamanlılık” olarak bilinen tanıdık kilitler, çünkü işlemin kesintiye uğrayacağını varsayarlar, bu nedenle paylaşılan bir kaynak edininceye kadar çalışmaya başlamayın. Karşılaştırma ve değiştirme işleminde, başlangıçta kaydettiğiniz özel bir “kanarya” değeri kullanırsınız, daha sonra sonunda altınızda bir değişiklik olmadığından emin olun; fikir, eğer başka bir iş parçacığı gelirse, kanarya öldürecek, bu yüzden işleminizi yeniden denemek için biliyorum. Bu, kendi kodunuzun da atomik olmasını gerektirir - paylaşılan duruma ara sonuçlar yazamazsınız, tamamen başarılı olmanız veya tamamen başarısız olmanız gerekir (sanki herhangi bir işlem yapmamışsınız gibi).

cevap 2:

Tamamen farklı iki şey. İş parçacığı güvenliği, her iş parçacığı başka bir iş parçacığının çalışmasını bozmadan birçok farklı iş parçacığı tarafından tekrar tekrar çağrılabilecek şekilde yazılmış bir işlev anlamına gelir (örneğin, başka bir iş parçacığının kullandığı bir değişkenin değerini değiştirerek)

Atomik (eğer bununla gittiğiniz yere ulaşırsam) bir nesnenin bir örneğinin oluşturulması anlamına gelir - bu yüzden ne sıklıkta bahsedilirse gönderilsin, her zaman bir örneği görürsünüz (herhangi bir iş parçacığından)


cevap 3:

Atomik işlemler, dahili olarak atomik işlemleri kullanan Mutexes veya Semaphores gibi bir çeşit kilit kullanarak veya atomik ve bellek çitleri kullanarak kilitsiz senkronizasyon uygulayarak iplik güvenliğini elde etmenin bir yoludur.

Bu nedenle, ilkel veri türlerindeki atomik işlemler, iplik güvenliğini sağlamak için bir araçtır, ancak normalde birbirine dayanan birden fazla işleminiz olduğundan, iplik güvenliğini otomatik olarak sağlamaz. Mutexes gibi bu işlemlerin kesintisiz yapılmasını sağlamalısınız.

Evet, bu atomik veri türlerinden birini c # olarak yazmak iş parçacığı için güvenlidir, ancak iş parçacığı içinde kullandığınız işlevi güvenli hale getirmez. Yalnızca tek bir yazma işleminin "aynı anda" ikinci bir iş parçacığına erişmesine rağmen doğru yürütülmesini sağlar. Daha azı, şimdiki iş parçacığından bir sonraki okumanın daha önce farklı bir iş parçacığı olarak yazılmış olabileceği değeri elde etmesi garanti edilmez, sadece okunan değerin geçerli olması sağlanır.