Bu gerçekten bir refakat değil ; bir işlevi aynı iş parçacığında (veya farklı iş parçacıklarında) iki kez çalıştırmıyorsunuz . Bunu özyineleme yoluyla veya geçerli işlevin adresini geri arama işlevi işaretçisi arg olarak başka bir işleve geçirerek alabilirsiniz. (Ve güvensiz olmaz çünkü senkron olur).
Bu, bir sinyal işleyici ve ana iş parçacığı arasında sadece düz vanilya veri yarışı UB'dir (Tanımsız Davranış): sadece sig_atomic_tbunun için güvenli olduğu garanti edilir . 8 baytlık bir nesnenin x86-64 üzerinde bir komutla yüklenebileceği veya saklanabileceği gibi, derleyici de bu grubu seçerken diğerleri işe yarayabilir. (@ İcarus'un cevabının gösterdiği gibi).
Bkz. MCU programlama - döngü sırasında C ++ O2 optimizasyonu kesiliyor - tek çekirdekli bir mikro denetleyicideki bir kesme işleyicisi temel olarak tek bir dişli programdaki bir sinyal işleyici ile aynı şeydir. Bu durumda UB'nin sonucu, bir yükün bir döngüden kaldırılmasıdır.
Veri yarışı UB nedeniyle gerçekte yırtılma test durumunuz muhtemelen 32 bit modunda veya yapı üyelerini ayrı ayrı yükleyen eski bir derleyici derleyicisi ile geliştirildi / test edildi.
Sizin durumunuzda, derleyici depoları sonsuz döngüden optimize edebilir, çünkü hiçbir UB içermeyen program bunları gözlemleyemez. datadeğil _Atomicya davolatile , ve döngüde başka hiçbir yan etkisi yoktur. Yani herhangi bir okuyucunun bu yazıcıyla senkronize edebilmesi mümkün değil. Bu aslında optimizasyon etkinken derlerseniz gerçekleşir ( Godbolt ana alt kısmında boş bir döngü gösterir). Ayrıca yapıyı ikiye değiştirdim long longve gcc movdqa, döngüden önce tek bir 16 baytlık depo kullanıyor . (Bu garanti atomik değildir , ancak pratikte neredeyse tüm CPU'larda, hizalı olduğu varsayılarak veya Intel'de sadece bir önbellek sınırını geçmez. X86'da neden doğal olarak hizalanmış bir değişken atomda tamsayı ataması var? )
Dolayısıyla optimizasyon etkinken derleme de testinizi kıracak ve size her seferinde aynı değeri gösterecektir. C taşınabilir bir montaj dili değildir.
volatile struct two_intayrıca derleyiciyi onları optimize etmemeye zorlar , ancak tüm yapıyı atomik olarak yüklemeye / depolamaya zorlamaz. (Bu olmaz durdurmak Not. Gerçi ya, bunu yaparken onu) volatileyok değil veri yarış UB önlemek, ama pratikte arası iplik iletişim için yeterli ve insanlar (satır içi asm) ile birlikte elle sarılmış atomics inşa nasıldı normal CPU mimarileri için C11 / C ++ 11'den önce. Onlar ediyoruz önbellek tutarlı öylesine volatileolduğu için çoğunlukla benzer pratikte _Atomicilememory_order_relaxed saf yük ve saf-mağaza için, türleri için kullanılan eğer yırtılması alamadım bu yüzden derleyici tek talimat kullanacağı yeterince dar. Ve tabi kivolatileISO C standardından, aynı _Atomicmodayı kullanan ve mo_relaxed ile derleyen kod yazma garantisine sahip değildir .
Yaptığın bir işlevi olsaydı global_var++;, bir de intya long longana çalıştırmak olduğunu ve bir sinyal işleyici gelen uyumsuz, yani veri yarış UB oluşturmak için kullanımı yeniden entrancy bir yolunu olacaktır.
Nasıl derlendiğine bağlı olarak (bellek varış yeri inc veya ekleme veya ayrı yükleme / inc / depolama için), aynı iş parçasındaki sinyal işleyicilerine göre atomik veya değil olacaktır. Bkz . 'İnt num' için num ++ atomik olabilir mi? x86 ve C ++ 'da atomisite hakkında daha fazla bilgi için. (C11 stdatomic.hve _Atomicözniteliği C ++ 11'in std::atomic<T>şablonuna eşdeğer işlevsellik sağlar )
Bir komutun ortasında bir kesinti veya başka bir istisna olamaz, bu nedenle bellek-hedef ekleme atomik wrt'tur. bağlam anahtarları tek çekirdekli bir CPU üzerinde. Yalnızca (önbellek uyumlu) bir DMA yazarı, tek çekirdekli bir CPU'da öneki add [mem], 1olmayan bir lockartıştan "adım atabilir" . Başka bir iş parçacığının üzerinde çalışabileceği başka hiçbir çekirdek yoktur.
Bu, sinyallere benzer: sinyali işleyen ipliğin normal yürütülmesi yerine bir sinyal işleyici çalışır , bu nedenle bir komutun ortasında işlenemez.