├── README.md ├── async.cpp ├── async_buffer.cpp ├── atomics1.cpp ├── cv.cpp ├── datarace.cpp ├── sfml_grid.cpp ├── thread1.cpp ├── thread2.cpp ├── thread3.cpp ├── thread4.cpp ├── thread5.cpp ├── thread6.cpp └── trylock.cpp /README.md: -------------------------------------------------------------------------------- 1 | # modern cpp concurrency 2 | 3 | Code examples from my videos 4 | -------------------------------------------------------------------------------- /async.cpp: -------------------------------------------------------------------------------- 1 | // @file async.cpp 2 | #include 3 | #include 4 | 5 | int square(int x){ 6 | return x*x; 7 | } 8 | 9 | int main(){ 10 | 11 | std::future asyncFunction = std::async(&square,12); 12 | for(int i=0; i< 10; i++){ 13 | std::cout << square(i) << std::endl; 14 | } 15 | 16 | // We are blocked at the 'get()' operation, until our 17 | // result has computed 18 | int result = asyncFunction.get(); 19 | 20 | std::cout << "result is: " << result << std::endl; 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /async_buffer.cpp: -------------------------------------------------------------------------------- 1 | // @file async_buffer.cpp 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | bool bufferedFileLoader(){ 8 | size_t bytesLoaded = 0; 9 | while(bytesLoaded < 20000){ 10 | std::cout << "thread: loading file..." << std::endl; 11 | std::this_thread::sleep_for(std::chrono::milliseconds(250)); 12 | bytesLoaded += 1000; 13 | } 14 | return true; 15 | } 16 | 17 | int main(){ 18 | 19 | std::future backgroundThread = std::async(std::launch::async, 20 | bufferedFileLoader); 21 | 22 | std::future_status status; 23 | // Our main program loop 24 | while(true){ 25 | std::cout << "Main thread is running" << std::endl; 26 | // artificial sleep for our program 27 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 28 | status = backgroundThread.wait_for(std::chrono::milliseconds(1)); 29 | // If our data is ready, that is, our background 30 | // thread has completed 31 | if(status == std::future_status::ready){ 32 | std::cout << "Our data is ready..." << std::endl; 33 | break; 34 | } 35 | 36 | } 37 | 38 | std::cout << "Program is complete" << std::endl; 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /atomics1.cpp: -------------------------------------------------------------------------------- 1 | // @file atomics.cpp 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static std::atomic shared_value= 0; 8 | 9 | void shared_value_increment(){ 10 | shared_value+=1; 11 | } 12 | 13 | int main(){ 14 | 15 | std::vector threads; 16 | for(int i=0; i < 1000; i++){ 17 | threads.push_back(std::thread(shared_value_increment)); 18 | } 19 | 20 | for(int i=0; i < 1000; i++){ 21 | threads[i].join(); 22 | } 23 | 24 | std::cout << "Shared value:" << shared_value << std::endl; 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /cv.cpp: -------------------------------------------------------------------------------- 1 | // @file cv.cpp 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | std::mutex gLock; 9 | std::condition_variable gConditionVariable; 10 | 11 | int main(){ 12 | 13 | int result = 0; 14 | bool notified = false; 15 | 16 | // Reporting thread 17 | // Must wait on work, done by the working thread 18 | std::thread reporter([&]{ 19 | std::unique_lock lock(gLock); 20 | if(!notified){ 21 | gConditionVariable.wait(lock); 22 | } 23 | std::cout << "Reporter, result is: " << result << std::endl; 24 | }); 25 | 26 | // Working thread 27 | std::thread worker([&]{ 28 | std::unique_lock lock(gLock); 29 | // Do our work, because we have the lock 30 | result = 42 + 1 + 7; 31 | // Our work is done 32 | notified = true; 33 | std::this_thread::sleep_for(std::chrono::seconds(5)); 34 | std::cout << "Work complete\n"; 35 | // Wake up a thread, that is waiting, for some 36 | // condition to be true 37 | gConditionVariable.notify_one(); 38 | }); 39 | 40 | reporter.join(); 41 | worker.join(); 42 | 43 | std::cout << "Program complete" << std::endl; 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /datarace.cpp: -------------------------------------------------------------------------------- 1 | // @file datarace.cpp 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static int shared_value= 0; 8 | 9 | void shared_value_increment(){ 10 | shared_value = shared_value + 1; 11 | } 12 | 13 | int main(){ 14 | 15 | std::vector threads; 16 | for(int i=0; i < 1000; i++){ 17 | threads.push_back(std::thread(shared_value_increment)); 18 | } 19 | 20 | for(int i=0; i < 1000; i++){ 21 | threads[i].join(); 22 | } 23 | 24 | std::cout << "Shared value:" << shared_value << std::endl; 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /sfml_grid.cpp: -------------------------------------------------------------------------------- 1 | // @file sfml_grid.cpp 2 | // g++ -std=c++17 sfml_grid.cpp -o prog -lpthread -lsfml-graphics -lsfml-window -lsfml-system 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include // for fill 8 | #include 9 | #include // For sleep 10 | 11 | // Third party libraries 12 | #include 13 | 14 | // Globally available grid 15 | static std::vector grid; 16 | // Global array of all of our objects 17 | std::vector> shapes; 18 | // Keeps track of the program running 19 | bool isRunning = true; 20 | 21 | // Function to update grid 22 | // This will be handled in each thread, independently working 23 | // on the task. 24 | void update_grid(int x, int y){ 25 | while(isRunning){ 26 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 27 | grid[y*2+x] = rand()%4; 28 | } 29 | } 30 | 31 | 32 | // Entry point into our program 33 | int main(){ 34 | // Initialize our grid with 4 entries 35 | grid.reserve(4); 36 | std::fill(begin(grid),end(grid),0); 37 | // Pick out the shape being drawn 38 | // in our grid and update it accordingly. 39 | for(int x=0; x < 2; x++){ 40 | for(int y=0; y<2; y++){ 41 | shapes.push_back(std::make_unique(100.0f)); 42 | } 43 | } 44 | 45 | // Launch threads 46 | std::vector threads; 47 | for(int x=0; x < 2; x++){ 48 | for(int y=0; y<2; y++){ 49 | threads.push_back(std::thread(update_grid,x,y)); 50 | } 51 | } 52 | 53 | // Main program loop 54 | sf::RenderWindow window(sf::VideoMode(400, 400), "SFML with C++ threads"); 55 | 56 | // Main Game loop 57 | while (window.isOpen() && isRunning) 58 | { 59 | sf::Event event; 60 | while (window.pollEvent(event)) 61 | { 62 | if (event.type == sf::Event::Closed){ 63 | window.close(); 64 | isRunning = false; 65 | } 66 | } 67 | 68 | // Clear the window 69 | window.clear(); 70 | for(int x=0; x < 2; x++){ 71 | for(int y=0; y<2; y++){ 72 | // Set the position 73 | shapes[y*2+x]->setPosition(x*200,y*200); 74 | // Update the color 75 | if(0==grid[y*2+x]){ 76 | shapes[y*2+x]->setFillColor(sf::Color::Red); 77 | }else if(1==grid[y*2+x]){ 78 | shapes[y*2+x]->setFillColor(sf::Color::Green); 79 | }else if(2==grid[y*2+x]){ 80 | shapes[y*2+x]->setFillColor(sf::Color::Blue); 81 | }else if(3==grid[y*2+x]){ 82 | shapes[y*2+x]->setFillColor(sf::Color::White); 83 | } 84 | } 85 | } 86 | 87 | // Draw all of our shapes 88 | for(auto& shape: shapes){ 89 | window.draw(*shape); 90 | } 91 | window.display(); 92 | } 93 | 94 | // Join threads before program execution termintes 95 | for(auto& th: threads){ 96 | th.join(); 97 | } 98 | 99 | // Program finish 100 | std::cout << "Program Terminating" << std::endl; 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /thread1.cpp: -------------------------------------------------------------------------------- 1 | // @file thread1.cpp 2 | #include 3 | #include 4 | 5 | void test(int x){ 6 | std::cout << "Hello from thread!" << std::endl; 7 | std::cout << "Argument passed in: " << x << std::endl; 8 | } 9 | 10 | 11 | int main(){ 12 | 13 | std::thread myThread(&test, 100); 14 | myThread.join(); 15 | 16 | std::cout << "hello from my main thread" << std::endl; 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /thread2.cpp: -------------------------------------------------------------------------------- 1 | // @file thread2.cpp 2 | #include 3 | #include 4 | int main(){ 5 | 6 | auto lambda=[](int x){ 7 | std::cout << "Hello from thread!" << std::endl; 8 | std::cout << "Argument passed in: " << x << std::endl; 9 | }; 10 | 11 | std::thread myThread(lambda, 100); 12 | myThread.join(); 13 | 14 | std::cout << "hello from my main thread" << std::endl; 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /thread3.cpp: -------------------------------------------------------------------------------- 1 | // @file thread3.cpp 2 | #include 3 | #include 4 | #include 5 | 6 | int main(){ 7 | 8 | auto lambda=[](int x){ 9 | std::cout << "Hello from thread!" << std::this_thread::get_id() << std::endl; 10 | std::cout << "Argument passed in: " << x << std::endl; 11 | }; 12 | 13 | std::vector threads; 14 | for(int i=0; i < 10; i++){ 15 | threads.push_back(std::thread(lambda, i)); 16 | } 17 | 18 | for(int i=0; i < 10; i++){ 19 | threads[i].join(); 20 | } 21 | 22 | std::cout << "hello from my main thread" << std::endl; 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /thread4.cpp: -------------------------------------------------------------------------------- 1 | // @file thread4.cpp 2 | #include 3 | #include 4 | #include 5 | 6 | int main(){ 7 | 8 | auto lambda=[](int x){ 9 | std::cout << "Hello from thread!" << std::this_thread::get_id() << std::endl; 10 | std::cout << "Argument passed in: " << x << std::endl; 11 | }; 12 | 13 | std::vector jthreads; 14 | for(int i=0; i < 10; i++){ 15 | jthreads.push_back(std::jthread(lambda, i)); 16 | } 17 | 18 | 19 | std::cout << "hello from my main thread" << std::endl; 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /thread5.cpp: -------------------------------------------------------------------------------- 1 | // @file thread5.cpp 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::mutex gLock; 8 | static int shared_value= 0; 9 | 10 | void shared_value_increment(){ 11 | gLock.lock(); 12 | shared_value = shared_value + 1; 13 | gLock.unlock(); 14 | } 15 | 16 | int main(){ 17 | 18 | std::vector threads; 19 | for(int i=0; i < 1000; i++){ 20 | threads.push_back(std::thread(shared_value_increment)); 21 | } 22 | 23 | for(int i=0; i < 1000; i++){ 24 | threads[i].join(); 25 | } 26 | 27 | std::cout << "Shared value:" << shared_value << std::endl; 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /thread6.cpp: -------------------------------------------------------------------------------- 1 | // @file thread5.cpp 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | std::mutex gLock; 8 | static int shared_value= 0; 9 | 10 | void shared_value_increment(){ 11 | std::lock_guard lockGuard(gLock); 12 | shared_value = shared_value + 1; 13 | } 14 | 15 | int main(){ 16 | 17 | std::vector threads; 18 | for(int i=0; i < 1000; i++){ 19 | threads.push_back(std::thread(shared_value_increment)); 20 | } 21 | 22 | for(int i=0; i < 1000; i++){ 23 | threads[i].join(); 24 | } 25 | 26 | std::cout << "Shared value:" << shared_value << std::endl; 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /trylock.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | std::mutex g_lock; 7 | 8 | void job1(){ 9 | g_lock.lock(); 10 | std::cout << "job1 is executed" << std::endl; 11 | g_lock.unlock(); 12 | } 13 | 14 | void job2(){ 15 | if(g_lock.try_lock()){ 16 | std::cout << "job2 is executed" << std::endl; 17 | g_lock.unlock(); 18 | }else{ 19 | using namespace std::chrono_literals; 20 | std::this_thread::sleep_for(5s); 21 | if(g_lock.try_lock()){ 22 | std::cout << "job2 exected on 2nd try\n"; 23 | g_lock.unlock(); 24 | } 25 | } 26 | } 27 | 28 | int main(){ 29 | 30 | std::thread thread1(job1); 31 | std::thread thread2(job2); 32 | 33 | thread1.join(); 34 | thread2.join(); 35 | 36 | return 0; 37 | } 38 | --------------------------------------------------------------------------------