@ü.li:Listing 1: Beispiel für die Verwendung von std::atomic_flag @li:#include #include class Spinlock{ std::atomic_flag flag; public: Spinlock(): flag(ATOMIC_FLAG_INIT) {} void lock(){ while( flag.test_and_set() ); } void unlock(){ flag.clear(); } }; Spinlock spin; void workOnResource(){ spin.lock(); // shared resource spin.unlock(); } int main(){ std::thread t(workOnResource); std::thread t2(workOnResource); t.join(); t2.join(); } @ü.li:Listing 2: Zeigerarithmetik mit einem atomaren Zeiger @li:int intArray[5]; std::atomic p(intArray); p++; assert(p.load() == &intArray[1]); p+=1; assert(p.load() == &intArray[2]); --p; assert(p.load() == &intArray[1]); @ü.li:Listing 3: Implementierung der generischen fetch_mult-Funktion @li:template T fetch_mult(std::atomic& shared, T mult){ T oldValue= shared.load(); shared.compare_exchange_strong(oldValue, oldValue * mult); return oldValue; } int main(){ std::atomic myInt{5}; std::cout << myInt << std::endl; // 5 fetch_mult(myInt,"5"); std::cout << myInt << std::endl; // 25 } @ü.lit:Listing 4: Die generische fetch_mult-Funktion für integrale Typen !!! die zweite Zeile "requires ..." bitte farblich hervorheben @li:template requires std::is_integral::value T fetch_mult(std::atomic& shared, T mult){ T oldValue= shared.load(); shared.compare_exchange_strong(oldValue, oldValue * mult); return oldValue; } @: @ü.li:Listing 5: Consumer und Producer Threads, synchronisiert mit sequenzieller Konsistenz !!! die Zeile "work= "done";" bitte farblich hervorheben @li:#include #include #include #include std::string work; std::atomic ready(false); void consumer(){ while(!ready.load()){} std::cout<< work << std::endl; // done } void producer(){ work= "done"; ready=true; } int main(){ std::thread prod(producer); std::thread con(consumer); prod.join(); con.join(); } @ü.lit:Listing 6: Spinlock mit Acquire-Release-Semantik !!! die folgenden Zeilen bitte farblich hervorheben: !!! while( flag.test_and_set(std::memory_order_acquire) ); !!! flag.clear(std::memory_order_release); @li:#include #include class Spinlock{ std::atomic_flag flag; public: Spinlock(): flag(ATOMIC_FLAG_INIT) {} void lock(){ while( flag.test_and_set(std::memory_order_acquire) ); } void unlock(){ flag.clear(std::memory_order_release); } }; ... // siehe Listing 1 @ü.li:Listing 7: Zähler mit Relaxed-Semantik !!! die folgenden Zeilen bitte farblich hervorheben: !!! cnt.fetch_add(1, std::memory_order_relaxed); !!! t.join(); @li:#include #include #include #include std::atomic cnt = {0}; void f() { for (int n = 0; n < 1000; ++n) { cnt.fetch_add(1, std::memory_order_relaxed); } } int main() { std::vector v; for (int n = 0; n < 10; ++n) { v.emplace_back(f); } for (auto& t : v) { t.join(); } std::cout << "Final counter value is " << cnt << '\n'; } @ü.li:Listing 8: Kritischer Wettlauf mit unsynchronisierten Threads @li:int x= 0; int y= 0; void writing(){ x= 2000; y= 11; } void reading(){ std::cout << "y: " << y << " "; std::cout << "x: " << x << std::endl; } int main(){ std::thread thread1(writing); std:.thread thread2(reading); thread1.join(); thread2.join(); }; @ü.li:Listing 9: Synchronisation zweier Threads mit Mutexen !!! die folgenden Zeilen bitte farblich hervorheben: !!! std::lock_guard guard(mut); !!! std::lock_guard guard(mut) @li:int x= 0; int y= 0; std::mutex mut; void writing(){ std::lock_guard guard(mut); x= 2000; y= 11; } void reading(){ std::lock_guard guard(mut) std::cout << "y: " << y << " "; std::cout << "x: " << x << std::endl; } ... // siehe Listing 8 @ü.li:Listing 10: atomare load- und store-Operationen @li:std::atomic x= 0; std::atomic y= 0; void writing(){ x.store(2000); y.store(11); } void reading(){ std::cout << y.load() << " "; std::cout << x.load() << endl; } ... // siehe Listing 8 @ü.li:Listing 11: atomare Operationen mit Acquire-Release- und Relaxed-Semantik @li:std::atomic x= 0; std::atomic y= 0; void writing(){ x.store(2000,std::memory_order_relaxed); y.store(11, std::memory_order_release); } void reading(){ std::cout << y.load(std::memory_order_acquire) << " "; std::cout << x.load(std::memory_order_relaxed) << std::endl; } ... // siehe Listing 8 @ü.li:Listing 12: kritischer Wettlauf um x @li:int x= 0; std::atomic y= 0; void writing(){ x= 2000; y.store(11, std::memory_order_release); } void reading(){ std::cout << y.load(std::memory_order_acquire) << " "; std::cout << x << std::endl; } ... // siehe Listing 8 @ü.li:Listing 13: Relaxed-Semantik für alle Operationen @li:std::atomic x= 0; std::atomic y= 0; void writing(){ x.store(2000,std::memory_order_relaxed); y.store(11, std::memory_order_relaxed); } void reading(){ std::cout << y.load(std::memory_order_relaxed) << " "; std::cout << x.load(std::memory_order_relaxed) << endl; } ... // siehe Listing 8