2013-08-02 12 views
6

Bir C veya C++ programında, 2 iş parçacığı aynı genel değişkeni kullanıyorsa, bu değeri bir muteks aracılığıyla kilitlemeniz gerekir.hangi durumlarda bir değişkeni eşzamanlı erişimden kilitlemem gerekiyor?

Ama hangi durumlarda tam olarak?

  1. Konu 1: Konu 2 okuyun: Konu 2 yazma: yazma Konu 2: Eğer davasına kilitlemek için gereken Tabii

yazma

  • Konu 1 okumak
  • Konu 1 okumak 3 ama diğer 2 vaka ile nedir? Durum 2'de ne olur (atomik olmayan işlemler ile)? Bir tür erişim ihlali var mı yoksa Thread 2 eski değeri mi alıyor? Bu konuda şaşkınlık duyuyorum, çünkü donanım seviyesindeki bellek ve kayıtlar aynı anda erişilemiyor (normal PC donanımında) ya da paralel ram çiplerine paralel bus hatları olan bazı paralel CPU'larımız var mı?

  • +11

    Vaka 2 ve 3, yani en az bir ipliğin en az bir başka iplik okuma veya yazma ile yazdığı herhangi bir durum. – juanchopanza

    +0

    Farklı iş parçacıklarından veriye erişirken her zaman kilitlenmeniz gerekmez. Atomik işlemlere ve özellikle C++ 11'in std :: atomic ' – nijansen

    +0

    'a bakın Ve evet, son cümlenizi ele almak için çok çekirdekli CPU'larda, çeşitli önbellek düzeylerinde her bir değerin birden çok kopyası olabilir. – Hulk

    cevap

    8

    Sadece her durumda neler olabileceğini düşünün. Sadece ırk durumunu ele alalım: bu kolay ve sonuçlarımızı görmemiz yeterli.

    1 durumunda, değişken değiştirilmiyor, dolayısıyla hangi sırada olursa olsun, her iki iplik aynı değeri okuyacaktır. Yani temelde burada hiçbir şey yanlış değil.

    2 ve 3 numaralı vakalar kötüdür. Diyelim ki bir yarış durumunuz var ve hangi evrelerin daha erken erişebileceğini bilmiyorum. Bu şu anlama gelir:

    Durum 2 için: Tüm işlemlerin sonunda değişkenin değeri iyidir (iş parçacığı 1 tarafından yazılan değer olacaktır), ancak iş parçacığı 2 değişkenin eski bir değerini alabilir; Bir kazaya ya da başka sorunlara neden olabilir.

    Durum 3 için: Değişkenin son değeri öngörülebilir değildir, çünkü hangi iş parçacığının en son yazma işlemini gerçekleştireceğine bağlıdır.

    2 ve 3 numaralı durumlarda, iş parçacığından birinin tutarsız bir halde iken değişkene erişmeye çalıştığı ve iş parçacığı tarafından okunan bazı çöp verileriyle sonuçlanabileceği de olabilir (örn. Durum 2), hatta tüm işlemlerin tamamlanmasından sonra değişkende çöp verileri.

    Yani evet, kilitleme için vakalarda 2 için kilit ve 3.

    +3

    Vaka 2 oldukça kötü olabilir: değişken, iplik 2 tarafından okunduğunda tutarsız bir durumda olabilir. – juanchopanza

    +0

    @juanchopanza, evet, bu doğrudur (vaka 3 için de), fakat cevabın aşırı karmaşık olmadığını söylemedim . Cevabı ona eklemem mi, yoksa olduğu gibi bırakmalı mıyım? – SingerOfTheFall

    +4

    Sanırım bunu cevaba eklemem gerekiyor, atomik olmayan işlemlerde ana sorun bu. Cevabınız atomikliğe işaret ediyor gibi görünüyor. Ayrıca, kilitlemenin belirli bir erişim talimatını garanti etmeyeceğini de unutmayın. – juanchopanza

    3

    Kurallar basit: Eğer aynı anda başka bir iş parçacığı tarafından erişilen bir değişken, yazarsan

    , o zaman doğru bir şekilde sıralanmasının ihtiyaç işlemler (örneğin, kilitleme yoluyla). Bu basit kuralla

    , kolayca durumların her değerlendirebilir:

    1. Hayır yazma, senkronizasyon
    2. Yaz, eşzamanlı erişim gerek, senkronizasyon
    3. yaz ihtiyaç eşzamanlı erişim senkronizasyona ihtiyacınız var

    Ve eğer kilitlemezseniz ne olur?

    Eh, resmen bu tanımsız davranış olduğunu. Bilmediğimiz anlamı. Hangi tek olası tepki olsa da sorunun kapsamını kavramak yardımcı olur.

    • okuma durumunda: okuma durumunda bir bayat değeri
    • erişim: kısmi değerini kullanma (sadece yarısı güncellenir olabilecekler makine düzeyinde

      ya olduğu Eğer bakmak zamanı)
    • yazma durumunda: nihai değer bit düzeyinde (bir hodgepodge) değerlerine

    ... ve unutmayalım ki yanlış işaretçi/boyut okumak zaman bir cr yol açabilir kül.

    Derleyici düzeyinde, derleyici, iş parçacığının tek erişime sahipmiş gibi 'u en iyileştirebilir. Bu durumda anlamına gelebilir: "gereksiz" okur çıkarmadan

    • : if (flag) while (true) içine while (flag) dönüşümü ... çıkarmadan
    • "kullanılmayan"
    • yazıyor ...

    belleğin varlığı çitler ve açık senkronizasyon talimatları (mutekslerin kullanımı) bu optimizasyonları önler. bir amacı birden fazla parçacığı tarafından (okuma veya yazma) erişilebilir olup olmadığını ve senkronize edilmesi gerekir kere daha sonra , herhangi bir parçacığı bütün modifiye edilir:

    2

    Kural basittir. Aksi takdirde, davranışı tanımsız var.

    0

    iki durumlar dışında, görünse de oldukça basit değildir: hiç eşitlemek gerekmez

    sadece okuyucuları ve hiçbir yazarlar vardır
    1. , sen yoktur.
    2. Birkaç iş parçacığı yazıldığı zaman ve bunlardan en az biri okuma-değiştirme-yazma işlemi gerçekleştiriyorsa (örneğin, ++x;) her zaman eşitlemeniz gerekir, yoksa tamamen tahmin edilemeyen sonuçlar alırsınız. Diğer tüm durumlarda

    , sen en az bir yazar ve okuyucuyu (veya birkaç yazarları) varsa, genellikle (çok az istisna dışında) erişimini eşitlemek gerekir, ama mutlaka her zaman ve her zaman mümkün olan en sıkı şekilde değil.

    Çok kadara ihtiyacın garanti bağlıdır. Bazı uygulamalar (ve bazen hatta kilit adalet gerekir) parçacığı üzerinde sıkı sıralı tutarlılık gerekir. Bazı uygulamalar eşit derecede iyi çalışır, ancak çok daha iyi performansa sahip, sadece varsa olur-öncekiyle aynı iş parçacığı içinde teminat olacaktır. Yine diğer uygulamalar bile o kadar ihtiyaç ve rahat operasyonlarla ya da hiç bir teminat olmaksızın tamamen mutlu değil.Bu, herhangi bir senkronizasyon olmadan gayet iyi çalışır

    // worker thread 
    running = true; 
    while(running) { task = pull_task(); execute(task); } 
    
    // main thread exit code 
    running = false; 
    join(workerthread); 
    

    :

    Örneğin, bir iş parçacığı bu "tipik" uygulaması bir yazar ve bir okuyucu vardır. Evet, akıcı bir şekilde konuşmak gerekirse, running'un değeri ne zaman değişecek, ne de gerçekte hiçbir fark yaratmayacaktır. Bellek konumunun "rasgele" bir ara değere sahip olmasının hiçbir yolu yoktur ve bu değişiklik, iş parçacığının büyük olasılıkla bir görevi yürütmekle meşgul olabileceğinden, değişikliklerin birkaç düzine nanosaniye daha erken veya daha sonra görünüp görünmeyeceği gerçekten önemli değildir. En kötü durumda, birkaç milisaniye sonra değişikliği alır. Sonunda, bir sonraki yinelemede, numaralı çalışan iş parçacığı değişikliğini alacaktır ve çıkacaktır.
    Dr. Dobb'ın birkaç yıl önce yayınlanan SPSC hızlı ileri kuyruğu benzer bir prensip üzerinde çalıştı, sadece işaretçilerle çalıştı.

    GCC documentation adresinde birçok farklı senkronizasyon modu ve sonuçları hakkında iyi ve kapsamlı bir okuma verilir.

    İlgili konular