"Uçucu" anahtar kelime ne için kullanılır?


130

volatileAnahtar kelime hakkında bazı makaleler okudum ama doğru kullanımını bulamadım. C # ve Java'da ne için kullanılması gerektiğini bana söyler misiniz?


1
Uçucu ile ilgili sorunlardan biri, birden fazla anlama gelmesidir. Müthiş optimizasyonlar yapmamak derleyiciye bilgi sağlamak bir C mirasıdır. Bu , aynı zamanda bellek engeller erişim kullanılabilir gerektiği anlamına gelir. Ancak çoğu durumda sadece performansa mal olur ve / veya insanların kafasını karıştırır. : P
AnorZaken

Yanıtlar:


93

Hem C # hem de Java için "uçucu", derleyiciye bir değişkenin değerinin programın kendi kapsamı dışında değişebileceği için hiçbir zaman önbelleğe alınmaması gerektiğini söyler. Derleyici, değişken "kontrolünün dışında" değişirse sorunlara neden olabilecek optimizasyonlardan kaçınacaktır.


@Tom - gerektiği gibi not edildi, efendim - ve değiştirildi.
A

11
Hala bundan çok daha ince.
Tom Hawtin - tackline

1
Yanlış. Önbelleğe almayı engellemez. Cevabımı gör.
doug65536

168

Şu örneği düşünün:

int i = 5;
System.out.println(i);

Derleyici, aşağıdaki gibi 5'i yazdırmak için bunu optimize edebilir:

System.out.println(5);

Ancak, değişebilecek başka bir iş parçacığı varsa i, bu yanlış davranıştır. Başka bir iş parçacığı i6 olarak değişirse , optimize edilmiş sürüm yine de 5 yazdıracaktır.

volatileAnahtar bu optimizasyonu ve önbelleğe alma önler ve böylece değişken bir iş parçacığı tarafından değiştirilebilir yararlıdır.


3
Optimizasyonun iolarak işaretlenerek hala geçerli olacağına inanıyorum volatile. Java'da her şey önceden olan ilişkilerle ilgilidir.
Tom Hawtin - tackline

Gönderdiğiniz için teşekkürler, bir şekilde değişken kilitleme ile bağlantıları var mı?
Mircea

@Mircea: Bir şeyi uçucu olarak işaretlemenin tamamen ilgili olduğu söylendi: Bir alanı uçucu olarak işaretlemek, iş parçacıklarının verilen değişken için tutarlı bir değer görmesine izin vermek için bazı dahili mekanizmalar kullanır, ancak bu yukarıdaki cevapta belirtilmemiştir. ... belki birisi bunu onaylayabilir mi onaylamaz mı? Teşekkürler
npinti

5
@Sjoerd: Bu örneği anladığımdan emin değilim. Eğer iyerel bir değişkendir, başka hiçbir iş parçacığı yine de değiştirebilir. Bir alansa, derleyici, olmadığı sürece aramayı optimize edemez final. Derleyicinin, bir alanın finalaçıkça bildirilmediğinde "göründüğünü" varsayarak optimizasyonlar yapabileceğini sanmıyorum .
polygenelubricants

1
C # ve java C ++ değildir. Bu doğru değil. Önbelleğe almayı engellemez ve optimizasyonu engellemez. Zayıf sıralı bellek mimarilerinde gerekli olan okuma-edinme ve saklama-yayınlama semantiği ile ilgilidir. Spekülatif infazla ilgili.
doug65536

40

Uçucunun bir değişkene ne yaptığını anlamak için, değişken uçucu olmadığında ne olduğunu anlamak önemlidir.

  • Değişken Uçucu Değildir

İki evre A ve B geçici olmayan bir değişkene erişirken, her evre değişkenin yerel bir kopyasını yerel önbelleğinde tutacaktır. A iş parçacığı tarafından yerel önbelleğinde yapılan herhangi bir değişiklik, iş parçacığı B tarafından görünmez.

  • Değişken uçucudur

Değişkenler uçucu olarak bildirildiğinde, bu, esasen, iş parçacıklarının böyle bir değişkeni önbelleğe almaması gerektiği veya başka bir deyişle, evrelerin, doğrudan ana bellekten okunmadıkça bu değişkenlerin değerlerine güvenmemesi gerektiği anlamına gelir.

Peki, bir değişken ne zaman uçucu hale getirilir?

Birçok iş parçacığı tarafından erişilebilen bir değişkene sahip olduğunuzda ve her iş parçacığının, değer başka bir iş parçacığı / işlem / programın dışında güncellense bile, o değişkenin en son güncellenmiş değerini almasını istiyorsanız.


2
Yanlış. "Önbelleğe almayı önlemek" ile ilgisi yoktur. Derleyici tarafından VEYA CPU donanımının spekülatif yürütme yoluyla yeniden sıralanmasıyla ilgilidir.
doug65536

37

Uçucu alanların okumaları anlam kazanır . Bu, uçucu değişkenden okunan belleğin sonraki bellek okumalarından önce gerçekleşeceğinin garanti edildiği anlamına gelir. Derleyicinin yeniden sıralamayı yapmasını engeller ve eğer donanım gerektiriyorsa (zayıf sıralı CPU), donanımın uçucu okumadan sonra gerçekleşen ancak spekülatif olarak erken başlatılan tüm okumaları temizlemesi için özel bir talimat kullanacaktır, aksi takdirde CPU olabilir Yük alma sorunu ile emekliye ayrılma arasında herhangi bir spekülatif yük oluşmasını önleyerek, ilk etapta erken yayınlanmasını önlemek.

Uçucu alanların yazıları serbest bırakma anlamlarına sahiptir . Bu, uçucu değişkene herhangi bir bellek yazma işleminin, önceki tüm bellek yazma işlemlerinin diğer işlemciler tarafından görülebilene kadar geciktirilmesinin garanti edildiği anlamına gelir.

Aşağıdaki örneği düşünün:

something.foo = new Thing();

fooBir sınıftaki üye değişkendiyse ve diğer CPU'lar tarafından atıfta bulunulan nesne örneğine erişime sahipse , yapıcıdaki bellek yazmaları genel olarak görünür olmadan öncesomething değer foodeğişikliğini görebilirler! "Zayıf sıralı bellek" bu demektir. Bu, derleyicinin yapıcıdaki tüm depolara depodan önce sahip olsa bile oluşabilir . Eğer bir o kadar mağaza açma anlambilim sahip olacak ve donanım garanti yazma öncesi yazıyor bütün bunlara yazma izin vermeden önce diğer işlemciler tarafından görülebilir oluşmaya.Thingfoofoovolatilefoofoofoo

fooYazıların bu kadar kötü bir şekilde yeniden sıralanması nasıl mümkün olabilir? Önbellek satırı tutma fooyeri önbellekteyse ve yapıcıdaki mağazalar önbelleği kaçırdıysa, mağazanın önbelleğe yazılanlardan çok daha erken tamamlanması mümkündür.

Intel'in (berbat) Itanium mimarisi hafızayı zayıf bir şekilde sipariş etmişti. Orijinal XBox 360'ta kullanılan işlemcinin belleği yetersizdi. Çok popüler ARMv7-A dahil olmak üzere birçok ARM işlemcisi, hafızayı zayıf bir şekilde sipariş etti.

Geliştiriciler genellikle bu veri yarışlarını görmezler çünkü kilitler gibi şeyler tam bir bellek bariyeri yapar, esasen aynı anda anlambilim edinme ve yayınlama ile aynı şeydir. Kilidin içindeki hiçbir yük, kilit alınmadan önce spekülatif olarak yürütülemez, kilit alınana kadar ertelenir. Bir kilit serbest bırakma sürecinde hiçbir mağaza geciktirilemez, kilidi serbest bırakan talimat, kilit içinde yapılan tüm yazmalar genel olarak görünür olana kadar ertelenir.

Daha eksiksiz bir örnek "Çift kontrol edilmiş kilitleme" modelidir. Bu modelin amacı, bir nesneyi tembel olarak başlatmak için her zaman bir kilit elde etmek zorunda kalmamaktır.

Wikipedia'dan alıntı:

public class MySingleton {
    private static object myLock = new object();
    private static volatile MySingleton mySingleton = null;

    private MySingleton() {
    }

    public static MySingleton GetInstance() {
        if (mySingleton == null) { // 1st check
            lock (myLock) {
                if (mySingleton == null) { // 2nd (double) check
                    mySingleton = new MySingleton();
                    // Write-release semantics are implicitly handled by marking
                    // mySingleton with 'volatile', which inserts the necessary memory
                    // barriers between the constructor call and the write to mySingleton.
                    // The barriers created by the lock are not sufficient because
                    // the object is made visible before the lock is released.
                }
            }
        }
        // The barriers created by the lock are not sufficient because not all threads
        // will acquire the lock. A fence for read-acquire semantics is needed between
        // the test of mySingleton (above) and the use of its contents. This fence
        // is automatically inserted because mySingleton is marked as 'volatile'.
        return mySingleton;
    }
}

Bu örnekte, MySingletonyapıcıdaki depolar , depodan önce diğer işlemciler tarafından görülmeyebilir mySingleton. Böyle bir durumda, mySingleton'a göz atan diğer iş parçacıkları bir kilit elde etmeyecek ve yazımları kurucuya almaları gerekmeyecektir.

volatileasla önbelleğe almayı engellemez. Yaptığı şey, diğer işlemcilerin yazma "gördükleri" sırayı garantilemektir. Bir mağaza sürümü, bekleyen tüm yazmalar tamamlanana ve diğer işlemcilere, ilgili satırları önbelleğe almışlarsa önbellek satırlarını atmalarını / geri yazmalarını söyleyen bir veri yolu döngüsü verilinceye kadar bir mağazayı erteleyecektir. Yük alımı, tahmin edilen tüm okumaları temizleyerek bunların geçmişten eski değerler olmamasını sağlar.


İyi açıklama. Ayrıca iyi bir çift kontrol kilitleme örneği. Ancak, önbelleğe alma özellikleri konusunda endişelendiğim için ne zaman kullanacağımdan hala emin değilim. Yalnızca 1 iş parçacığının yazılacağı ve yalnızca 1 iş parçacığının okunacağı bir kuyruk uygulaması yazarsam, kilitler olmadan geçip sadece baş ve kuyruk "işaretçilerimi" geçici olarak işaretleyebilir miyim? Hem okuyucunun hem de yazarın en güncel değerleri görmesini sağlamak istiyorum.
nickdu

Hem headve tailihtiyaç varsayarak gelen yapımcı önlemek için uçucu olması tailolmaz değişikliği ve varsayarak tüketiciyi önlemek için headdeğişiklik olmaz. Ayrıca, headkuyruğa yazılan verilerin deponun headgenel olarak görünür hale gelmesinden önce global olarak görünür olmasını sağlamak için geçici olmalıdır .
doug65536

+1, En son / "en güncel" gibi terimler maalesef tekil doğru değer kavramını ima ediyor. Gerçekte, iki rakip bir bitiş çizgisini aynı anda geçebilir - bir cpu'da iki çekirdek aynı anda bir yazma talebinde bulunabilir . Sonuçta, çekirdekler sırayla iş yapmaz - bu, çok çekirdeği anlamsız hale getirir. İyi çok iş parçacıklı düşünme / tasarım, düşük seviyeli "en yeniliği" zorlamaya odaklanmamalı - doğal olarak sahte çünkü bir kilit, çekirdeği her seferinde adalet olmadan keyfi olarak bir hoparlör seçmeye zorlar - bunun yerine, böyle doğal olmayan bir konsepte ihtiyaç var.
AnorZaken

34

Uçucu anahtar kelime hem Java ve C # farklı anlamlara gelir.

Java

Gönderen Java Dil Spec :

Bir alan geçici olarak tanımlanabilir, bu durumda Java bellek modeli tüm iş parçacıklarının değişken için tutarlı bir değer görmesini sağlar.

C #

Uçucu anahtar kelimedeki C # Referansından :

Volatile anahtar sözcüğü, programdaki bir alanın işletim sistemi, donanım veya eşzamanlı olarak yürütülen bir iş parçacığı gibi bir şey tarafından değiştirilebileceğini belirtir.


Gönderdiğiniz için çok teşekkür ederim, Java'da anladığım gibi bu değişkeni bir iş parçacığı bağlamında kilitlemek gibi davranıyor ve C #'da değişkenin değeri yalnızca programdan değiştirilemez, işletim sistemi gibi dış faktörler de değerini değiştirebilir ( herhangi bir kilitleme ima edilmemiştir) ... Bu farklılıkları doğru anladıysam lütfen bana bildirin ...
Mircea

@ Mircea Java'da kilitleme yoktur, sadece uçucu değişkenin en güncel değerinin kullanılmasını sağlar.
krock

Java, bir tür bellek engeli vaat ediyor mu, yoksa C ++ ve C # gibi, yalnızca referansı uzaklaştırmamayı vaat ediyor mu?
Steven Sudit

Hafıza engeli bir uygulama detayıdır. Java'nın gerçekte vaat ettiği şey, tüm okumaların en son yazılan değeri görecek olmasıdır.
Stephen C

1
@StevenSudit Evet, donanım bir bariyer veya yükleme / edinme veya saklama / serbest bırakma gerektiriyorsa bu talimatları kullanacaktır. Cevabımı gör.
doug65536

9

Java'da, JVM'ye değişkenin aynı anda birden çok iş parçacığı tarafından kullanılabileceğini söylemek için "uçucu" kullanılır, bu nedenle belirli ortak optimizasyonlar uygulanamaz.

Özellikle aynı değişkene erişen iki iş parçacığının aynı makinedeki ayrı CPU'larda çalıştığı durum. CPU'ların tuttuğu verileri agresif bir şekilde önbelleğe alması çok yaygındır çünkü bellek erişimi önbellek erişiminden çok daha yavaştır. Bu, veri CPU1'de güncellenirse , önbellek kendini temizlemeye karar verdiğinde bunun yerine hemen tüm önbelleklerden ve ana belleğe geçmesi gerektiği anlamına gelir , böylece CPU2 güncellenmiş değeri görebilir (yine yoldaki tüm önbellekleri göz ardı ederek).


1

Uçucu olmayan verileri okurken, çalıştırılan iş parçacığı her zaman güncellenmiş değeri alabilir veya almayabilir. Ancak nesne uçucuysa, iş parçacığı her zaman en güncel değeri alır.


1
Cevabınızı yeniden ifade edebilir misiniz?
Anirudha Gupta

volatile anahtar kelime, önbelleğe alınmış değerden ziyade size en güncel değeri verecektir.
Subhash Saini

0

Volatile, eşzamanlılık sorununu çözmektir. Bu değeri senkronize yapmak için. Bu anahtar kelime çoğunlukla bir threading içinde kullanılır. Birden çok iş parçacığı aynı değişkeni güncellediğinde.


1
Sorunu "çözdüğünü" sanmıyorum. Bazı durumlarda yardımcı olan bir araçtır. Yarış koşullarında olduğu gibi, bir kilidin gerekli olduğu durumlar için uçucuya güvenmeyin.
Scratte
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.