2013-10-17 17 views
5

AFAIK C++ atomik (<atomic>) ailesinin 3 avantajları sağlar:C++ atomik ve çapraz iplik görüş

  • ilkel talimatları bölünmez (hiçbir kirli okur),
  • bellek sipariş (her ikisi de, CPU ve derleyici) ve Çapraz iş parçacığı görünürlüğü/değişimi yayılımı
  • .

Ayrıca üçüncü mermi hakkında emin değilim, bu nedenle aşağıdaki örneğe bakın.

#include <atomic> 

std::atomic_bool a_flag = ATOMIC_VAR_INIT(false); 
struct Data { 
    int x; 
    long long y; 
    char const* z; 
} data; 

void thread0() 
{ 
    // due to "release" the data will be written to memory 
    // exactly in the following order: x -> y -> z 
    data.x = 1; 
    data.y = 100; 
    data.z = "foo"; 
    // there can be an arbitrary delay between the write 
    // to any of the members and it's visibility in other 
    // threads (which don't synchronize explicitly) 

    // atomic_bool guarantees that the write to the "a_flag" 
    // will be clean, thus no other thread will ever read some 
    // strange mixture of 4bit + 4bits 
    a_flag.store(true, std::memory_order_release); 
} 

void thread1() 
{ 
    while (a_flag.load(std::memory_order_acquire) == false) {}; 
    // "acquire" on a "released" atomic guarantees that all the writes from 
    // thread0 (thus data members modification) will be visible here 
} 

void thread2() 
{ 
    while (data.y != 100) {}; 
    // not "acquiring" the "a_flag" doesn't guarantee that will see all the 
    // memory writes, but when I see the z == 100 I know I can assume that 
    // prior writes have been done due to "release ordering" => assert(x == 1) 
} 

int main() 
{ 
    thread0(); // concurrently 
    thread1(); // concurrently 
    thread2(); // concurrently 

    // join 

    return 0; 
} 

Öncelikle kodunda (özellikle thread2) benim varsayımları doğrulamak ediniz.

İkincisi, benim sorular şunlardır:

  1. Nasıl a_flag diğer çekirdeklere yaymak yazar? std::atomic diğer çekirdekler önbelleği ile yazar önbelleğinde a_flag senkronize mu

  2. (mesi kullanarak veya başka bir şey) ya da yayılma otomatiktir?

  3. Belli bir makinede bir bayrak için yazının atomik olduğunu varsayın (x86'da int_32'yi düşünün) VE Senkronize edilmek üzere özel bir belleğimiz yok (yalnızca bir bayrağa sahibiz) atomik kullanmamız gerekecek mi?

  4. dikkate alarak en popüler işlemci mimarisi (86, IA-64 KOL v.whatever 64), (ama potansiyel olarak gecikmiş) otomatik çapraz çekirdek görüş (şimdi değil dikkate reorderings AM), veya herhangi bir veri parçasını yaymak için belirli komutlar vermeniz gerekiyor mu? kendileri önemi yok

cevap

2
  1. Çekirdekler. Soru, "tüm çekirdekler nihayetinde aynı bellek güncelleştirmesini görüyor", yani donanımınızın sizin için yaptığı bir şeydir (örn. Önbellek tutarlılığı protokolleri). Tek bir bellek var, bu nedenle asıl sorun, donanımın özel bir endişesi olan önbellekleme. Bu sorunun belirsiz olduğu görülmektedir. Önemli olan, bir senkronizasyon noktası olan ve thread0 ve thread1'un belirli bir sırada görünmesine neden olan a_flag yükünün ve deposunun oluşturduğu satın alma serbest bırakma çiftidir (yani ürününün ürününden önce her şey'den önce gerçekleşir. thread1'daki döngüden sonra.

  2. Evet, aksi halde eşitleme noktanız olmaz.

  3. C++ 'da herhangi bir "komut" a ihtiyacınız yoktur. C++, belirli bir CPU türü üzerinde çalıştığı gerçeğinin farkında bile değil. Muhtemelen bir Rubik küpünde yeterli hayal gücü ile bir C++ programı çalıştırabilirsiniz. Bir C++ derleyici, C++ bellek modeli tarafından açıklanan senkronizasyon davranışını uygulamak için gerekli yönergeleri ve talimat kilitleme öneklerini ve bellek çitlerini içeren x86 üzerinde ve ayrıca yönergeleri çok fazla yeniden düzenlememeyi seçer.X86 güçlü bir şekilde sipariş edilmiş bir bellek modeline sahip olduğundan, yukarıdaki kod atomik olmayan naif, yanlış olanla karşılaştırıldığında en az ek kod üretmelidir.

  4. Kodunuzda thread2 bulunması tüm programın tanımlanmamış davranışını yapar.


Sadece eğlence için, ve kendinizi iyi örnek olabilir için neler olup bittiğini çalışma, üç varyasyonları kodunu derlenmiş olduğunu göstermek için. (Ben bir glbbal int x ekledim ve thread1 ekledim x = data.y;).

Edinme/Yayın: (kodunuzu)

thread0: 
    mov DWORD PTR data, 1 
    mov DWORD PTR data+4, 100 
    mov DWORD PTR data+8, 0 
    mov DWORD PTR data+12, OFFSET FLAT:.LC0 
    mov BYTE PTR a_flag, 1 
    ret 

thread1: 
.L14: 
    movzx eax, BYTE PTR a_flag 
    test al, al 
    je .L14 
    mov eax, DWORD PTR data+4 
    mov DWORD PTR x, eax 
    ret 

sırayla tutarlı: (kaldırmak açık sipariş)

thread0: 
    mov eax, 1 
    mov DWORD PTR data, 1 
    mov DWORD PTR data+4, 100 
    mov DWORD PTR data+8, 0 
    mov DWORD PTR data+12, OFFSET FLAT:.LC0 
    xchg al, BYTE PTR a_flag 
    ret 

thread1: 
.L14: 
    movzx eax, BYTE PTR a_flag 
    test al, al 
    je .L14 
    mov eax, DWORD PTR data+4 
    mov DWORD PTR x, eax 
    ret 

"Naif": (sadece bool kullanarak)

thread0: 
    mov DWORD PTR data, 1 
    mov DWORD PTR data+4, 100 
    mov DWORD PTR data+8, 0 
    mov DWORD PTR data+12, OFFSET FLAT:.LC0 
    mov BYTE PTR a_flag, 1 
    ret 

thread1: 
    cmp BYTE PTR a_flag, 0 
    jne .L3 
.L4: 
    jmp .L4 
.L3: 
    mov eax, DWORD PTR data+4 
    mov DWORD PTR x, eax 
    ret 

Gördüğünüz gibi, büyük bir fark yoktur. "Yanlış" sürümü, aslında, yükü kaçırma dışında çoğunlukla doğru görünüyor (bellek işleneni ile cmp kullanıyor). Sıralı olarak tutarlı olan versiyon, örtülü bir kilit ön ekine sahip olan ve herhangi bir açık çitler gerektirmeyen xcgh talimatındaki kullanım kolaylığını gizler.