C # 'da uçucu anahtar kelimenin kullanımını gösteren resim


88

volatileAnahtar kelimenin davranışını görsel olarak gösteren küçük bir program kodlamak istiyorum . İdeal olarak, geçici olmayan bir statik alana aynı anda erişim sağlayan ve bu nedenle yanlış davranış gösteren bir program olmalıdır.

Uçucu anahtar sözcüğün aynı programa eklenmesi sorunu çözmelidir.

Bu başarmayı başaramadığım bir şey. Birkaç kez denemek, optimizasyonu etkinleştirmek vb. Bile, 'değişken' anahtar kelime olmadan her zaman doğru bir davranış elde ediyorum.

Bu konu hakkında bir fikriniz var mı? Basit bir demo uygulamasında böyle bir sorunu nasıl simüle edeceğinizi biliyor musunuz? Donanıma bağlı mı?

Yanıtlar:


102

Çalışan bir örnek elde ettim!

Ana fikir wiki'den alındı, ancak C # için bazı değişiklikler yapıldı. Wiki makalesi bunu C ++ 'ın statik alanı için gösterir, C # gibi her zaman dikkatli bir şekilde statik alanlara istekleri derler ... ve statik olmayan bir örnekle örnek yaparım:

Bu örneği Release modunda ve hata ayıklayıcı olmadan (yani Ctrl + F5 kullanarak) çalıştırırsanız, satır while (test.foo != 255)'while (true)' olarak optimize edilecek ve bu program asla geri dönmeyecektir. Ancak volatileanahtar kelime ekledikten sonra her zaman 'Tamam' alırsınız.

class Test
{
    /*volatile*/ int foo;

    static void Main()
    {
        var test = new Test();

        new Thread(delegate() { Thread.Sleep(500); test.foo = 255; }).Start();

        while (test.foo != 255) ;
        Console.WriteLine("OK");
    }
}

5
Hem x86 hem de x64 .NET 4.0 üzerinde test edilmiştir - örneğin hala uygulanabilir olduğunu doğrulayabilir. Teşekkürler! :)
Roman Starkov

1
Güzel! JIT'in bu tür bir optimizasyon yaptığını ve bu uçuculuğun onu devre dışı bıraktığını asla bilmiyordum.
usr

3
Açıkça belirtmek gerekirse, "uçucu" anahtar kelimeyi foo'nun beyanına ekliyorsunuz, değil mi?
JoeCool

21

Evet, donanıma bağlıdır (sorunu birden fazla işlemci olmadan görme olasılığınız düşüktür), ancak aynı zamanda uygulamaya da bağlıdır. CLR spesifikasyonundaki bellek modeli spesifikasyonları, CLR'nin Microsoft uygulamasının mutlaka yapmadığı şeylere izin verir.


6

'Uçucu' anahtar kelime belirtilmediğinde bu gerçekten bir hata meselesi değildir, daha çok belirtilmediğinde bir hata meydana gelebilir. Genel olarak, durumun ne zaman derleyiciden daha iyi olduğunu bileceksiniz!

Bunu düşünmenin en kolay yolu, derleyicinin, eğer isterse, belirli değerleri satır içi yapabilmesidir. Değeri uçucu olarak işaretleyerek, kendinize ve derleyiciye değerin gerçekten değişebileceğini söylüyorsunuz (derleyici öyle düşünmese bile). Bu, derleyicinin değerleri sıralı tutmaması, önbelleği tutmaması veya değeri erken okumaması (optimize etme girişiminde) anlamına gelir.

Bu davranış, C ++ 'daki ile gerçekten aynı anahtar kelime değildir.

MSDN'nin burada kısa bir açıklaması var . İşte Volatilite, Atomiklik ve İç içe geçme konularında belki de daha ayrıntılı bir yazı


4

Kod sanal bir makine tarafından soyutlandığı için C # ile göstermek zordur, bu nedenle bu makinenin bir uygulamasında uçucu olmadan çalışır, diğerinde başarısız olabilir.

Wikipedia'nın C ile nasıl gösterileceğine dair güzel bir örneği var.

JIT derleyicisi değişkenin değerinin zaten değişemeyeceğine karar verirse ve böylece artık onu kontrol etmeyen makine kodu yaratırsa, aynı şey C # içinde de olabilir. Şimdi başka bir iş parçacığı değeri değiştiriyorsa, ilk iş parçacığınız yine de döngüye yakalanmış olabilir.

Başka bir örnek ise Meşgul Bekleme'dir.

Yine, bu C # ile de olabilir, ancak büyük ölçüde sanal makineye ve JIT derleyicisine (veya JIT yoksa yorumlayıcıya bağlıdır ... teoride, MS her zaman bir JIT derleyicisi kullanır ve ayrıca Mono kullanır. bir; ancak manuel olarak devre dışı bırakmanız mümkün olabilir).


4

İşte bu davranışın kolektif anlayışına katkım ... Çok fazla değil, sadece değişken bir ayetin davranışını geçici olmayan (yani "normal") bir int değerini yan yana gösteren bir gösteri (xkip'in demosuna dayalı) -side, aynı programda ... Bu konuyu bulduğumda aradığım şey buydu.

using System;
using System.Threading;

namespace VolatileTest
{
  class VolatileTest 
  {
    private volatile int _volatileInt;
    public void Run() {
      new Thread(delegate() { Thread.Sleep(500); _volatileInt = 1; }).Start();
      while ( _volatileInt != 1 ) 
        ; // Do nothing
      Console.WriteLine("_volatileInt="+_volatileInt);
    }
  }

  class NormalTest 
  {
    private int _normalInt;
    public void Run() {
      new Thread(delegate() { Thread.Sleep(500); _normalInt = 1; }).Start();
      // NOTE: Program hangs here in Release mode only (not Debug mode).
      // See: http://stackoverflow.com/questions/133270/illustrating-usage-of-the-volatile-keyword-in-c-sharp
      // for an explanation of why. The short answer is because the
      // compiler optimisation caches _normalInt on a register, so
      // it never re-reads the value of the _normalInt variable, so
      // it never sees the modified value. Ergo: while ( true )!!!!
      while ( _normalInt != 1 ) 
        ; // Do nothing
      Console.WriteLine("_normalInt="+_normalInt);
    }
  }

  class Program
  {
    static void Main() {
#if DEBUG
      Console.WriteLine("You must run this program in Release mode to reproduce the problem!");
#endif
      new VolatileTest().Run();
      Console.WriteLine("This program will now hang!");
      new NormalTest().Run();
    }

  }
}

Yukarıda bazı gerçekten mükemmel kısa açıklamalar ve bazı harika referanslar var. Kafamı karıştırmama yardımcı oldukları için herkese teşekkürler volatile(en azından volatileilk içgüdülerimin nerede olduğuna güvenmeyeceğimi bilecek lockkadar).

Şerefe ve TÜM balıklar için teşekkürler. Keith.


Not: "Ben görmek istiyorum: Çok oldu Orijinal isteğin bir demo, ilgilenen olurdu bir statik uçucu int doğru yerde davrandığını bir statik int kural dışı bir davranışta.

Bu meydan okumayı denedim ve başarısız oldum. (Aslında çok çabuk pes ettim ;-). Statik değişkenlerle denediğim her şeyde, uçucu olup olmadıklarına bakılmaksızın "doğru" davranıyorlar ... ve durum buysa, NEDEN böyle bir açıklama yapmak isterim ... Öyle mi? derleyici, yazmaçlardaki statik değişkenlerin değerlerini önbelleğe almaz (yani, bunun yerine bu yığın adresine bir başvuruyu önbelleğe alır )?

Hayır, bu yeni bir soru değil ... bu, topluluğu orijinal soruya geri döndürme girişimi .


2

Joe Albahari'nin bana çok yardımcı olan aşağıdaki metnine rastladım.

Biraz değiştirdiğim yukarıdaki metinden statik bir uçucu alan oluşturarak bir örnek aldım. volatileAnahtar kelimeyi kaldırdığınızda, program süresiz olarak engellenecektir. Bu örneği Yayın modunda çalıştırın .

class Program
{
    public static volatile bool complete = false;

    private static void Main()
    {           
        var t = new Thread(() =>
        {
            bool toggle = false;
            while (!complete) toggle = !toggle;
        });

        t.Start();
        Thread.Sleep(1000); //let the other thread spin up
        complete = true;
        t.Join(); // Blocks indefinitely when you remove volatile
    }
}
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.