C++11 seminaari, kevät 2012 Johannes Koskinen
Sisältö Mikä onkaan ongelma? Standardidraftin luku 29: Atomiset tyypit Muistimalli
Rinnakkaisuus On multicore systems, when a thread writes a value to memory, it becomes immediately available for reading on the same core, but threads executing on other cores may see the previous value for some time, and when they get to see values change, it may not be in the same order as what other threads see. In addition, both C++ compilers and CPUs may reorder regular memory accesses within a single thread for efficiency.
Mitä tulee lopputulokseksi? http://channel9.msdn.com/events/goingnative/goingnative-2012/threads-and- Shared-Variables-in-C-11 Alussa kaikki ovat muuttujat nollia. Thread 1 Thread 2 x = 1; y = 1; r1 = y; r2 = x; Myös r1 = r2 = 0 on mahdollinen
Mitä tulee lopputulokseksi? Standardi Alussa kaikki ovat muuttujat nollia. Thread 1 Thread 2 r1 = y; r2 = x; x = r1; y = 42; Myös r1 = r2 = 42 on mahdollinen
Toimiiko? Thread 1 Thread 2 x = 42; while (!done) {} done = true; assert(x == 42);
Entä muuten? 1. struct { char a; char b; } x; 2. struct { int a:8; int b:8; } x; 3. struct { char a; int b:11; } x; 4. list<int> x;
Mutta onhan meillä semaforit&co Mutta synkronointi näiden avulla on usein liian raskasta (ja vaikeaa)
Atomic<T> Tarjoaa helpon tavan rinnakkaiseen käsittelyyn Kääntäjä tekee tarvittavat taiat, jotta homma toimii Voi olla myös hyvin raskasta ja riippua alla olevasta raudasta Onneksi x86 ja muut nykyaikaiset prosessorit selviävät kohtuullisen vähällä Atomic objects are the only C++ objects free of data races; that is, if one thread writes to an atomic while another thread reads from it, the behavior is well-defined.
Operaatioita Load, store Exchange Compare_exchange Fetch_add, Fetch_sub Fetch_and, Fetch_or, Fetch_xor Perinteiset operaattorit Ei kopiorakentajaa/sijoitusta atomicin kanssa Flag, Flag_test_and_set, Flag_clear
Compare_exchange template< class T > bool atomic_compare_exchange_strong(std::atomic<t>* object, T* expected, T desired) if (memcmp(object, expected, sizeof(*object)) == 0) memcpy(object, &desired, sizeof(*object)); else memcpy(expected, object, sizeof(*object)); Palauttaa vertailun tuloksen (true, jos samat)
Compare_exchange (Lock-free list) void append(list* s, node* n) { node* head; do { head = s->head; n->next = head; } while (! std::atomic_compare_exchange_weak(s->head, head, n)); }
Memory_order Välillä voidaan optimoida nopeuden suhteen (kaikkea ei tarvitse sarjallistaa) Each atomic operation accepts a std::memory_order as an additional parameter, which specifies how non-atomic memory accesses are to be ordered around this atomic operation. enum memory_order { memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst } memory_order;
http://en.cppreference.com/w/cpp/atomic/memory_order order_relaxed order_consume order_acquire order_release order_acq_rel order_seq_cst The operation does not order memory. Performs a consume operation on the affected memory location, marking the root of a data dependency tree. The reads from the affected memory location that carry data dependency cannot be reordered before the load; other reads can be. On most platforms, this affects compiler optimization only. Performs an acquire operation on the affected memory locations, thus making regular memory writes in other threads released through the atomic variable to which it is applied, visible to the current thread. No reads from the affected memory location can be reordered before the load. Performs a release operation on the affected memory locations, thus making regular memory writes visible to other threads through the atomic variable to which it is applied. The operation has both acquire and release semantics. The operation has both acquire and release semantics, and in addition, has sequentially-consistent operation ordering.
Esimerkkejä http://en.cppreference.com/w/cpp/atomic/memory_order