├── LICENCE ├── README.md ├── chapter_06_launching_a_thread ├── hello_arg.cc ├── hello_lambda.cc ├── hello_memfn.cc ├── hello_move.cc ├── hello_ref.cc └── hello_thread.cc ├── chapter_07_the_cpp_thread └── cpp_thread_exception.cc ├── chapter_08_managing_threads └── managing_threads_raii.cc ├── chapter_09_multiple_threads └── multiple_threads_launch.cc ├── chapter_10_data_races └── data_race_output.cc ├── chapter_11_working_with_shared_data ├── data_race_output_mutex.cc └── mutex_try_lock.cc ├── chapter_12_lock_guard └── data_race_output_lock_guard.cc ├── chapter_13_unique_lock └── data_race_output_unique_lock.cc ├── chapter_14_recursive_and_timed_mutexes ├── timed_mutex_try_lock_for.cc ├── timed_mutex_try_lock_until.cc └── timed_mutex_unique_lock.cc ├── chapter_15_shared_mutexes └── shared_mutex.cc ├── chapter_16_shared_data_initialization ├── singleton.cc └── singleton.h ├── chapter_17_double_checked_locking ├── call_once.cc └── call_once.h ├── chapter_18_thread_local_variables └── thread_local.cc ├── chapter_19_deadlock ├── adopt_lock.cc ├── deadlock.cc ├── defer_lock.cc ├── reversed_locking_order.cc └── scoped_lock.cc ├── chapter_20_livelock └── livelock.cc ├── chapter_22_condition_variables └── condition.cc ├── chapter_23_condition_variables_and_predicates └── condition_predicate.cc ├── chapter_24_futures_and_promises ├── future_promise.cc └── promise_except.cc ├── chapter_25_shared_futures └── promise_shared_future.cc ├── chapter_26_atomic_operations └── count.cc ├── chapter_27_atomic_types ├── atomic_count.cc └── volatile_count.cc ├── chapter_29_spin_locks └── spin.cc ├── chapter_30_lock_free_programming ├── singleton_atomic.cc └── singleton_atomic.h ├── chapter_32_packaged_tasks └── packaged_task.cc ├── chapter_33_the_std_async_function ├── async_exception.cc ├── async_hello.cc └── async_launch.cc ├── chapter_37_execution_policies ├── non_policy_execution.cc ├── parallel_execution_policy.cc ├── parallel_unsequenced_execution_policy.cc └── sequenced_execution_policy.cc ├── chapter_38_algorithms_and_execution_policies └── except_policy.cc ├── chapter_39_parallel_algorithms └── transform_reduce.cc ├── chapter_40_concurrent_queue_implementation ├── concurrent_queue.h ├── concurrent_queue_cv.h ├── concurrent_queue_cv_main.cc └── concurrent_queue_main.cc └── chapter_42_thread_pool_implementation ├── thread_pool.cc ├── thread_pool.h └── thread_pool_main.cc /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 James Raynard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multithreading-Book 2 | Source code for the examples in the e-book version of "Learn Multithreading with Modern C++". Available now from leading online stores, including Amazon Kindle, Apple Books and Kobo. 3 | 4 | To see all outlets, including local versions of Amazon, click here. 5 | 6 | Now also available in paperback and hardback from Amazon! 7 | -------------------------------------------------------------------------------- /chapter_06_launching_a_thread/hello_arg.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Thread entry point now takes an std::string argument 6 | void hello(std::string str) 7 | { 8 | std::cout << str << "\n"; 9 | } 10 | 11 | int main() 12 | { 13 | // Create an std::thread object and initialize it 14 | // with the entry point function 15 | // Pass the argument to hello() 16 | std::thread t{hello, "Hello, Thread"}; 17 | 18 | // Wait for the thread to complete 19 | t.join(); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /chapter_06_launching_a_thread/hello_lambda.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | 8 | // Use the member function pointer 9 | // Pass an object as the argument 10 | // to the member function call 11 | std::thread t{ []() 12 | { 13 | std::cout << "Hello, Thread!\n"; 14 | }}; 15 | 16 | t.join(); 17 | 18 | // Lambda expression with capture by reference 19 | std::string str{"abc"}; 20 | std::thread t2{ [&]() 21 | { 22 | str = "xyz"; 23 | }}; 24 | 25 | t2.join(); 26 | 27 | std::cout << "str is now " << str << "\n"; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /chapter_06_launching_a_thread/hello_memfn.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class greeter { 6 | public: 7 | // Thread entry point 8 | void hello() 9 | { 10 | std::cout << "Hello, Thread!\n"; 11 | } 12 | }; 13 | 14 | int main() 15 | { 16 | // Create an object of the class 17 | greeter greet; 18 | 19 | // Use the member function pointer 20 | // Pass an object as the argument 21 | // to the member function call 22 | std::thread t{&greeter::hello, &greet}; 23 | 24 | // Wait for the thread to complete 25 | t.join(); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /chapter_06_launching_a_thread/hello_move.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Thread entry point 6 | void func(std::string s) 7 | { 8 | std::cout << "All your string are belong to us: " 9 | << s << "\n"; 10 | } 11 | 12 | int main() 13 | { 14 | // This will be the argument to hello() 15 | std::string str{"abc"}; 16 | 17 | // Pass the argument by move 18 | std::thread t{func, std::move(str)}; 19 | 20 | // Wait for the thread to complete 21 | t.join(); 22 | 23 | // Verify that the string has been modified 24 | std::cout << "str is now " << str << "\n"; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /chapter_06_launching_a_thread/hello_ref.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Thread entry point now takes its argument by reference 6 | void hello(std::string& s) 7 | { 8 | s = "xyz"; 9 | } 10 | 11 | int main() 12 | { 13 | // This will be the argument to hello() 14 | std::string str{"abc"}; 15 | 16 | // Pass the argument by reference 17 | std::thread t{hello, std::ref(str)}; 18 | 19 | // Wait for the thread to complete 20 | t.join(); 21 | 22 | // Verify that the string has been modified 23 | std::cout << "str is now " << str << "\n"; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /chapter_06_launching_a_thread/hello_thread.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Thread entry point function 5 | void hello() 6 | { 7 | std::cout << "Hello, Thread!\n"; 8 | } 9 | 10 | int main() 11 | { 12 | // Create an std::thread object and initialize it 13 | // with a callable object 14 | // A function pointer to the entry point function 15 | std::thread t{hello}; 16 | 17 | // Wait for the thread to complete 18 | t.join(); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /chapter_07_the_cpp_thread/cpp_thread_exception.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Thread entry point 5 | void hello() 6 | { 7 | try { 8 | // Throw an exception 9 | throw std::exception(); 10 | } 11 | catch(std::exception& e) { 12 | // Handle the exception 13 | std::cout << "Caught an exception\n"; 14 | } 15 | std::cout << "Hello, Thread!\n"; 16 | } 17 | 18 | int main() 19 | { 20 | // Create the thread object 21 | std::thread t{hello}; 22 | t.join(); 23 | 24 | // Check that the program is still running 25 | std::cout << "Finished\n"; 26 | } 27 | -------------------------------------------------------------------------------- /chapter_08_managing_threads/managing_threads_raii.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class thread_guard { 5 | std::thread t; 6 | public: 7 | // explicit constructor prevents conversions 8 | // std::thread is move-only, so the 9 | // constructor takes an rvalue reference 10 | explicit thread_guard(std::thread t) : t(std::move(t)) {} 11 | 12 | ~thread_guard() 13 | { 14 | // Join the thread if not already joined 15 | if (t.joinable()) t.join(); 16 | } 17 | 18 | // Prevent copying by declaring 19 | // the copy operators as deleted 20 | 21 | thread_guard(const thread_guard&) = delete; 22 | thread_guard& operator=(const thread_guard&) = delete; 23 | }; 24 | 25 | // Callable object - thread entry point 26 | void hello() 27 | { 28 | std::cout << "Hello, Thread!\n"; 29 | } 30 | 31 | int main() 32 | { 33 | try { 34 | //std::thread t{ hello }; 35 | thread_guard tg{ std::thread(hello) }; 36 | throw std::exception(); 37 | } 38 | catch (std::exception& e) { 39 | std::cout << "Exception caught\n"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /chapter_09_multiple_threads/multiple_threads_launch.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void hello(int num) 6 | { 7 | auto delay = std::chrono::seconds(num); 8 | std::this_thread::sleep_for(delay); 9 | std::cout << "Hello from thread " << num << "\n"; 10 | } 11 | 12 | int main() 13 | { 14 | std::thread t1{hello, 1}; 15 | std::thread t2{hello, 2}; 16 | std::thread t3{hello, 3}; 17 | 18 | t1.join(); 19 | t2.join(); 20 | t3.join(); 21 | } 22 | -------------------------------------------------------------------------------- /chapter_10_data_races/data_race_output.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std::literals; 6 | 7 | auto print = [](const std::string& str) 8 | { 9 | for (int i = 0; i < 5; ++i) { 10 | std::cout << str[0] << str[1] << str[2] << "\n"; 11 | std::this_thread::sleep_for(50ms); 12 | } 13 | }; 14 | 15 | int main() 16 | { 17 | std::thread t1{print, "abc"}; 18 | std::thread t2{print, "def"}; 19 | std::thread t3{print, "xyz"}; 20 | 21 | t1.join(); 22 | t2.join(); 23 | t3.join(); 24 | } 25 | -------------------------------------------------------------------------------- /chapter_11_working_with_shared_data/data_race_output_mutex.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // Mutex to protect the critical region 9 | std::mutex print_mutex; 10 | 11 | auto print = [](const std::string& str) 12 | { 13 | for (int i = 0; i < 5; ++i) { 14 | // Lock the mutex before the critical region 15 | print_mutex.lock(); 16 | 17 | // The critical region, which modifies std::cout 18 | std::cout << str[0] << str[1] << str[2] << "\n"; 19 | 20 | // Unlock the mutex after the critical region 21 | print_mutex.unlock(); 22 | std::this_thread::sleep_for(50ms); 23 | } 24 | }; 25 | 26 | int main() 27 | { 28 | std::thread t1{print, "abc"}; 29 | std::thread t2{print, "def"}; 30 | std::thread t3{print, "xyz"}; 31 | 32 | t1.join(); 33 | t2.join(); 34 | t3.join(); 35 | } 36 | -------------------------------------------------------------------------------- /chapter_11_working_with_shared_data/mutex_try_lock.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | std::mutex the_mutex; 9 | 10 | void task1() { 11 | std::cout << "Task1 trying to get lock\n"; 12 | the_mutex.lock(); 13 | std::cout << "Task1 has lock\n"; 14 | std::this_thread::sleep_for(500ms); 15 | std::cout << "Task1 releasing lock\n"; 16 | the_mutex.unlock(); 17 | } 18 | 19 | void task2() { 20 | std::this_thread::sleep_for(100ms); 21 | std::cout << "Task2 trying to get lock\n"; 22 | while (!the_mutex.try_lock()) { 23 | std::cout << "Task2 could not get lock\n"; 24 | std::this_thread::sleep_for(100ms); 25 | } 26 | std::cout << "Task2 has lock\n"; 27 | the_mutex.unlock(); 28 | } 29 | 30 | int main() { 31 | std::thread t1{task1}, t2{task2}; 32 | t1.join(); 33 | t2.join(); 34 | } 35 | -------------------------------------------------------------------------------- /chapter_12_lock_guard/data_race_output_lock_guard.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // Mutex to protect the critical region 9 | std::mutex print_mutex; 10 | 11 | auto print = [](const std::string& str) 12 | { 13 | for (int i = 0; i < 5; ++i) { 14 | // Create the lock_guard object 15 | // This will lock the mutex 16 | std::lock_guard lk(print_mutex); 17 | 18 | // The critical region, which modifies std::cout 19 | std::cout << str[0] << str[1] << str[2] << "\n"; 20 | 21 | std::this_thread::sleep_for(50ms); 22 | } // Destroy the lock_guard object 23 | }; 24 | 25 | int main() 26 | { 27 | std::thread t1{print, "abc"}; 28 | std::thread t2{print, "def"}; 29 | std::thread t3{print, "xyz"}; 30 | 31 | t1.join(); 32 | t2.join(); 33 | t3.join(); 34 | } 35 | -------------------------------------------------------------------------------- /chapter_13_unique_lock/data_race_output_unique_lock.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // Mutex to protect the critical region 9 | std::mutex print_mutex; 10 | 11 | auto print = [](const std::string& str) 12 | { 13 | for (int i = 0; i < 5; ++i) { 14 | // Create the lock_guard object 15 | // This will lock the mutex 16 | std::unique_lock lk(print_mutex); 17 | 18 | // The critical region, which modifies std::cout 19 | std::cout << str[0] << str[1] << str[2] << "\n"; 20 | 21 | // Unlock the mutex 22 | lk.unlock(); 23 | 24 | std::this_thread::sleep_for(50ms); 25 | } // Destroy the unique_lock object 26 | }; 27 | 28 | int main() 29 | { 30 | std::thread t1{print, "abc"}; 31 | std::thread t2{print, "def"}; 32 | std::thread t3{print, "xyz"}; 33 | 34 | t1.join(); 35 | t2.join(); 36 | t3.join(); 37 | } 38 | -------------------------------------------------------------------------------- /chapter_14_recursive_and_timed_mutexes/timed_mutex_try_lock_for.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | std::timed_mutex the_mutex; 9 | 10 | void task1() 11 | { 12 | std::cout << "Task1 trying to get lock\n"; 13 | the_mutex.lock(); 14 | std::cout << "Task1 has lock\n"; 15 | std::this_thread::sleep_for(500ms); 16 | std::cout << "Task1 releasing lock\n"; 17 | the_mutex.unlock(); 18 | } 19 | 20 | void task2() 21 | { 22 | std::this_thread::sleep_for(100ms); 23 | std::cout << "Task2 trying to get lock\n"; 24 | while (!the_mutex.try_lock_for(100ms)) { 25 | std::cout << "Task2 could not get lock\n"; 26 | } 27 | std::cout << "Task2 has lock\n"; 28 | the_mutex.unlock(); 29 | } 30 | 31 | int main() 32 | { 33 | std::thread t1{task1}, t2{task2}; 34 | t1.join(); 35 | t2.join(); 36 | } 37 | -------------------------------------------------------------------------------- /chapter_14_recursive_and_timed_mutexes/timed_mutex_try_lock_until.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | std::timed_mutex the_mutex; 9 | 10 | void task1() 11 | { 12 | std::cout << "Task1 trying to get lock\n"; 13 | the_mutex.lock(); 14 | std::cout << "Task1 has lock\n"; 15 | std::this_thread::sleep_for(500ms); 16 | std::cout << "Task1 releasing lock\n"; 17 | the_mutex.unlock(); 18 | } 19 | 20 | void task2() 21 | { 22 | std::this_thread::sleep_for(100ms); 23 | std::cout << "Task2 trying to get lock\n"; 24 | 25 | // Find a timepoint 100ms in the future 26 | auto deadline = std::chrono::steady_clock::now() + 100ms; 27 | 28 | // Keep trying to lock until the deadline is reached 29 | while (!the_mutex.try_lock_until(deadline)) { 30 | std::cout << "Task2 could not get lock\n"; 31 | // Advance the timepoint by 100ms 32 | deadline += 100ms; 33 | } 34 | std::cout << "Task2 has lock\n"; 35 | the_mutex.unlock(); 36 | } 37 | 38 | int main() 39 | { 40 | std::thread t1{task1}, t2{task2}; 41 | t1.join(); 42 | t2.join(); 43 | } 44 | -------------------------------------------------------------------------------- /chapter_14_recursive_and_timed_mutexes/timed_mutex_unique_lock.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | std::timed_mutex the_mutex; 9 | 10 | void task1() 11 | { 12 | std::cout << "Task1 trying to get lock\n"; 13 | std::unique_lock lk(the_mutex); 14 | std::cout << "Task1 has lock\n"; 15 | std::this_thread::sleep_for(500ms); 16 | std::cout << "Task1 releasing lock\n"; 17 | } 18 | 19 | void task2() 20 | { 21 | std::this_thread::sleep_for(100ms); 22 | std::cout << "Task2 trying to get lock\n"; 23 | 24 | // Find a timepoint 100ms in the future 25 | auto deadline = std::chrono::steady_clock::now() + 100ms; 26 | std::unique_lock lk(the_mutex, deadline); 27 | 28 | // Keep trying to lock until the deadline is reached 29 | while (!lk.owns_lock()) { 30 | std::cout << "Task2 could not get lock\n"; 31 | 32 | // Advance the timepoint by 100ms 33 | deadline += 100ms; 34 | lk.try_lock_until(deadline); 35 | } 36 | std::cout << "Task2 has lock\n"; 37 | the_mutex.unlock(); 38 | } 39 | 40 | int main() 41 | { 42 | std::thread t1{task1}, t2{task2}; 43 | t1.join(); 44 | t2.join(); 45 | } 46 | -------------------------------------------------------------------------------- /chapter_15_shared_mutexes/shared_mutex.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std::literals; 9 | 10 | // The shared mutex object 11 | std::shared_timed_mutex shmut; 12 | 13 | // Mutex to protect std::cout 14 | std::mutex print_mutex; 15 | 16 | void write(int i) 17 | { 18 | { 19 | // Use print_mutex to protect std::cout 20 | std::lock_guard pg(print_mutex); 21 | std::cout << "Writer thread " << i 22 | << " trying to lock\n"; 23 | } 24 | 25 | // Get an exclusive lock on the shared mutex 26 | std::lock_guard lg(shmut); 27 | 28 | { 29 | // Use print_mutex to protect std::cout 30 | std::lock_guard pg(print_mutex); 31 | std::cout << "Writer thread " << i << 32 | " has exclusive lock\n"; 33 | } 34 | std::this_thread::sleep_for(2s); 35 | } 36 | 37 | void read(int i) 38 | { 39 | { 40 | // Use print_mutex to protect std::cout 41 | std::lock_guard pg(print_mutex); 42 | std::cout << "Reader thread " << i 43 | << " trying to lock\n"; 44 | } 45 | 46 | // Get a shared lock on the shared mutex 47 | std::shared_lock sl(shmut); 48 | 49 | { 50 | // Use print_mutex to protect std::cout 51 | std::lock_guard pg(print_mutex); 52 | std::cout << "Reader thread " << i 53 | << " has shared lock\n"; 54 | } 55 | } 56 | 57 | int main() 58 | { 59 | std::vector threads; 60 | 61 | for (int i = 0; i < 5; ++i) 62 | threads.push_back(std::thread{read, i}); 63 | 64 | threads.push_back(std::thread{write, 5}); 65 | threads.push_back(std::thread{write, 6}); 66 | 67 | for (int i = 7; i < 15; ++i) 68 | threads.push_back(std::thread{read, i}); 69 | 70 | for (auto& thr : threads) 71 | thr.join(); 72 | } 73 | -------------------------------------------------------------------------------- /chapter_16_shared_data_initialization/singleton.cc: -------------------------------------------------------------------------------- 1 | #include "singleton.h" 2 | #include 3 | #include 4 | 5 | // Define the static member variable 6 | Singleton *Singleton::single = 0; 7 | 8 | // Thread entry point function 9 | void task() 10 | { 11 | // Get the Singleton object 12 | Singleton* s = Singleton::get_singleton(); 13 | 14 | // Display its address 15 | std::cout << s << std::endl; 16 | } 17 | 18 | int main() 19 | { 20 | std::vector threads; 21 | for (int i = 0; i < 10; ++i) 22 | threads.push_back(std::thread{task}); 23 | for (auto& t : threads) 24 | t.join(); 25 | } 26 | -------------------------------------------------------------------------------- /chapter_16_shared_data_initialization/singleton.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLETON_H 2 | #define SINGLETON_H 3 | 4 | #include 5 | 6 | class Singleton { 7 | // Private static member 8 | static Singleton *single; 9 | public: 10 | // Static member function which 11 | // returns a Singleton object 12 | // This is the only way to get a Singleton 13 | static Singleton* get_singleton() 14 | { 15 | // If the member object does not already exist 16 | if (single == 0) { 17 | // Create the object 18 | single = new Singleton; 19 | } 20 | return single; 21 | } 22 | 23 | protected: 24 | // The constructor is protected and 25 | // can only be called by a static member function 26 | Singleton() 27 | { 28 | std::cout << "Initializing singleton" << "\n"; 29 | } 30 | }; 31 | 32 | #endif //SINGLETON_H 33 | -------------------------------------------------------------------------------- /chapter_17_double_checked_locking/call_once.cc: -------------------------------------------------------------------------------- 1 | #include "call_once.h" 2 | #include 3 | #include 4 | 5 | // Define the static member variable 6 | Singleton *Singleton::single = 0; 7 | 8 | // Thread entry point function 9 | void task() 10 | { 11 | // Get the Singleton object 12 | Singleton* s = Singleton::get_singleton(); 13 | 14 | // Display its address 15 | std::cout << s << std::endl; 16 | } 17 | 18 | int main() 19 | { 20 | std::vector threads; 21 | for (int i = 0; i < 10; ++i) 22 | threads.push_back(std::thread{task}); 23 | for (auto& t : threads) 24 | t.join(); 25 | } 26 | -------------------------------------------------------------------------------- /chapter_17_double_checked_locking/call_once.h: -------------------------------------------------------------------------------- 1 | #ifndef CALL_ONCE_H 2 | #define CALL_ONCE_H 3 | 4 | #include 5 | 6 | // Header for std::once_flag and std::call_once 7 | #include 8 | 9 | // Global object to store synchronization data 10 | // (Could also be static member of Singleton) 11 | std::once_flag single_flag; 12 | 13 | class Singleton { 14 | // Private static member 15 | static Singleton *single; 16 | public: 17 | // Static member function which 18 | // returns a Singleton object 19 | // This is the only way to get a Singleton 20 | static Singleton* get_singleton() 21 | { 22 | std::call_once(single_flag, []() { 23 | // Create the object 24 | single = new Singleton; 25 | }); 26 | 27 | return single; 28 | } 29 | 30 | protected: 31 | // The constructor is protected and 32 | // can only be called by a static member function 33 | Singleton() 34 | { 35 | std::cout << "Initializing singleton" << "\n"; 36 | } 37 | }; 38 | 39 | #endif //CALL_ONCE_H 40 | -------------------------------------------------------------------------------- /chapter_18_thread_local_variables/thread_local.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std::literals; 6 | 7 | // Each thread will have its own engine object. 8 | // Each object should generate the same sequence 9 | thread_local std::mt19937 mt; 10 | 11 | void func() { 12 | std::uniform_real_distribution dist(0, 1); // Doubles in the range 0 to 1 13 | 14 | for (int i = 0; i < 10; ++i) // Generate 10 random numbers 15 | std::cout << dist(mt) << ", "; 16 | } 17 | 18 | int main() { 19 | std::cout << "Thread 1's random values:" << "\n"; 20 | std::thread t1{ func }; 21 | t1.join(); 22 | std::this_thread::sleep_for(100ms); 23 | 24 | // Thread 2 should display the same sequence as thread 1 25 | std::cout << "\nThread 2's random values:" << "\n"; 26 | std::thread t2{ func }; 27 | t2.join(); 28 | std::cout << "\n"; 29 | } -------------------------------------------------------------------------------- /chapter_19_deadlock/adopt_lock.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // The mutex objects to be locked 9 | std::mutex mutex1; 10 | std::mutex mutex2; 11 | 12 | // C++11's std::lock tries to acquire both locks in a single operation. 13 | // We then call std::unique_lock's constructor with the std::adopt_lock option, 14 | // so it accepts a mutex which is already locked. 15 | 16 | // One thread "wins" and locks both mutexes. The other thread waits. 17 | // Eventually, the first thread unlocks both mutexes. 18 | // This allows the other thread to lock the mutexes and proceed. 19 | 20 | void func1() { 21 | std::cout << "Thread 1 locking mutexes..." << std::endl; 22 | std::lock(mutex1, mutex2); // Lock both mutexes 23 | std::cout << "Thread 1 has locked the mutexes..." << std::endl; 24 | std::unique_lock lk1(mutex1, std::adopt_lock); // Associate each mutex 25 | std::unique_lock lk2(mutex2, std::adopt_lock); // with a unique_lock 26 | std::cout << "Thread 1 releasing mutexes..." << std::endl; 27 | } 28 | 29 | void func2() { 30 | std::cout << "Thread 2 locking mutexes..." << std::endl; 31 | std::lock(mutex2, mutex1); // Lock both mutexes 32 | std::cout << "Thread 2 has locked the mutexes..." << std::endl; 33 | std::unique_lock lk1(mutex1, std::adopt_lock); // Associate each mutex 34 | std::unique_lock lk2(mutex2, std::adopt_lock); // with a unique_lock 35 | std::cout << "Thread 2 releasing mutexes..." << std::endl; 36 | } 37 | 38 | int main() { 39 | std::thread t1{ func1 }; 40 | std::thread t2{ func2 }; 41 | t1.join(); 42 | t2.join(); 43 | } -------------------------------------------------------------------------------- /chapter_19_deadlock/deadlock.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // The mutex objects to be locked 9 | std::mutex mutex1; 10 | std::mutex mutex2; 11 | 12 | // The threads try to lock the mutexes in a different order 13 | // This results in deadlock when each thread has to wait 14 | // for the other thread to unlock before it can proceed. 15 | 16 | // Lock mutex 1, then try to lock mutex 2 17 | void func1() { 18 | // Use std::endl to make sure the output is displayed on all systems 19 | std::cout << "Thread 1 locking mutex 1..." << std::endl; 20 | std::unique_lock lk1(mutex1); // Acquire lock on mutex1 21 | std::cout << "Thread 1 has locked mutex 1" << std::endl; 22 | std::this_thread::sleep_for(50ms); // Do some work 23 | std::cout << "Thread 1 locking mutex 2..." << std::endl; 24 | std::unique_lock lk2(mutex2); // Wait for lock on mutex2 25 | std::cout << "Thread 1 has locked mutex 2" << std::endl; 26 | std::this_thread::sleep_for(50ms); // Do some work 27 | std::cout << "Thread 2 releases locks" << std::endl; 28 | } 29 | 30 | // Lock mutex 2, then try to lock mutex 1 31 | void func2() { 32 | std::cout << "Thread 2 locking mutex 2..." << std::endl; 33 | std::unique_lock lk1(mutex2); // Acquire lock on mutex2 34 | std::cout << "Thread 2 has locked mutex 2" << std::endl; 35 | std::this_thread::sleep_for(50ms); // Do some work 36 | std::cout << "Thread 2 locking mutex 1..." << std::endl; 37 | std::unique_lock lk2(mutex1); // Wait for lock on mutex1 38 | std::cout << "Thread 2 has locked mutex 1" << std::endl; 39 | std::this_thread::sleep_for(50ms); // Do some work 40 | std::cout << "Thread 2 releases locks" << std::endl; 41 | } 42 | 43 | int main() { 44 | std::thread t1{ func1 }; 45 | std::thread t2{ func2 }; 46 | t1.join(); 47 | t2.join(); 48 | } -------------------------------------------------------------------------------- /chapter_19_deadlock/defer_lock.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // The mutex objects to be locked 9 | std::mutex mutex1; 10 | std::mutex mutex2; 11 | 12 | // We call std::unique_lock's constructor with the std::defer_lock option, 13 | // which allows the mutexes to be locked later. 14 | // C++11's std::lock then tries to acquire both locks in a single operation. 15 | 16 | // One thread "wins" and locks both mutexes. The other thread waits. 17 | // Eventually, the first thread unlocks both mutexes. 18 | // This allows the other thread to lock the mutexes and proceed. 19 | 20 | void func1() { 21 | std::cout << "Thread 1 locking mutexes..." << std::endl; 22 | std::cout << "Thread 1 has locked the mutexes..." << std::endl; 23 | std::unique_lock lk1(mutex1, std::defer_lock); // Associate each mutex with a lock 24 | std::unique_lock lk2(mutex2, std::defer_lock); // ...but don’t lock it yet 25 | std::lock(lk1, lk2); // Lock both mutexes 26 | std::cout << "Thread 1 releasing mutexes..." << std::endl; 27 | } 28 | 29 | void func2() { 30 | std::cout << "Thread 2 locking mutexes..." << std::endl; 31 | std::cout << "Thread 2 has locked the mutexes..." << std::endl; 32 | std::unique_lock lk1(mutex1, std::defer_lock); // Associate each mutex with a lock 33 | std::unique_lock lk2(mutex2, std::defer_lock); // ...but don’t lock it yet 34 | std::lock(lk2, lk1); // Lock both mutexes 35 | std::cout << "Thread 2 releasing mutexes..." << std::endl; 36 | } 37 | 38 | int main() { 39 | std::thread t1{ func1 }; 40 | std::thread t2{ func2 }; 41 | t1.join(); 42 | t2.join(); 43 | } -------------------------------------------------------------------------------- /chapter_19_deadlock/reversed_locking_order.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // The mutex objects to be locked 9 | std::mutex mutex1; 10 | std::mutex mutex2; 11 | 12 | // Both threads try to lock the mutexes in the same order 13 | // One thread "wins" and locks mutex 1, then mutex 2. The other thread waits. 14 | // Eventually, the first thread unlocks the mutexes. 15 | // This allows the other thread to lock mutex 1 and proceed. 16 | 17 | // Lock mutex 1, then try to lock mutex 2 18 | void func1() { 19 | // Use std::endl to make sure the output is displayed on all systems 20 | std::cout << "Thread 1 locking mutex 1..." << std::endl; 21 | std::unique_lock lk1(mutex1); // Acquire lock on mutex1 22 | std::cout << "Thread 1 has locked mutex 1" << std::endl; 23 | std::this_thread::sleep_for(50ms); // Do some work 24 | std::cout << "Thread 1 locking mutex 2..." << std::endl; 25 | std::unique_lock lk2(mutex2); // Wait for lock on mutex2 26 | std::cout << "Thread 1 has locked mutex 2" << std::endl; 27 | std::this_thread::sleep_for(50ms); // Do some work 28 | std::cout << "Thread 2 releases locks" << std::endl; 29 | } 30 | 31 | // Lock mutex 1, then try to lock mutex 2 32 | void func2() { 33 | std::cout << "Thread 2 locking mutex 1..." << std::endl; 34 | std::unique_lock lk2(mutex1); // Wait for lock on mutex1 35 | std::cout << "Thread 2 has locked mutex 1" << std::endl; 36 | std::this_thread::sleep_for(50ms); // Do some work 37 | std::cout << "Thread 2 locking mutex 2..." << std::endl; 38 | std::unique_lock lk1(mutex2); // Acquire lock on mutex2 39 | std::cout << "Thread 2 has locked mutex 2" << std::endl; 40 | std::this_thread::sleep_for(50ms); // Do some work 41 | std::cout << "Thread 2 releases locks" << std::endl; 42 | } 43 | 44 | int main() { 45 | std::thread t1{ func1 }; 46 | std::thread t2{ func2 }; 47 | t1.join(); 48 | t2.join(); 49 | } -------------------------------------------------------------------------------- /chapter_19_deadlock/scoped_lock.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // The mutex objects to be locked 9 | std::mutex mutex1; 10 | std::mutex mutex2; 11 | 12 | // C++17's std::scoped_lock tries to acquire both locks in a single operation 13 | // One thread "wins" and locks both mutexes. The other thread waits. 14 | // Eventually, the first thread unlocks both mutexes. 15 | // This allows the other thread to lock the mutexes and proceed. 16 | 17 | void func1() { 18 | std::cout << "Thread 1 locking mutexes..." << std::endl; 19 | std::scoped_lock lk1(mutex1, mutex2); 20 | std::cout << "Thread 1 has locked mutexes..." << std::endl; 21 | std::this_thread::sleep_for(50ms); 22 | std::cout << "Thread 1 releasing mutexes..." << std::endl; 23 | } 24 | 25 | void func2() { 26 | std::cout << "Thread 2 locking mutexes..." << std::endl; 27 | std::scoped_lock lk1(mutex2, mutex1); 28 | std::cout << "Thread 2 has locked mutexes..." << std::endl; 29 | std::this_thread::sleep_for(50ms); 30 | std::cout << "Thread 2 releasing mutexes..." << std::endl; 31 | } 32 | 33 | int main() { 34 | std::thread t1{ func1 }; 35 | std::thread t2{ func2 }; 36 | t1.join(); 37 | t2.join(); 38 | } -------------------------------------------------------------------------------- /chapter_20_livelock/livelock.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std::literals; 6 | 7 | std::timed_mutex mutex1, mutex2; 8 | 9 | void func1() 10 | { 11 | std::this_thread::sleep_for(10ms); 12 | bool locked{false}; 13 | while (!locked) { 14 | // Lock mutex1 15 | std::unique_lock lk(mutex1); 16 | std::this_thread::sleep_for(1s); 17 | std::cout << "After you, Claude!\n"; 18 | // Try to get a lock on mutex2 19 | locked = mutex2.try_lock_for(5ms); 20 | } 21 | if (locked) 22 | std::cout << "Thread1 has locked both mutexes"; 23 | } 24 | 25 | void func2() 26 | { 27 | bool locked{false}; 28 | while (!locked) { 29 | // Lock mutex2 30 | std::unique_lock lk(mutex2); 31 | std::this_thread::sleep_for(1s); 32 | std::cout << "No, after you, Cecil!\n"; 33 | // Try to get a lock on mutex1 34 | locked = mutex1.try_lock_for(5ms); 35 | } 36 | if (locked) 37 | std::cout << "Thread2 has locked both mutexes\n"; 38 | } 39 | 40 | int main() 41 | { 42 | std::thread t1(func1); 43 | std::this_thread::sleep_for(10ms); 44 | std::thread t2(func2); 45 | t1.join(); 46 | t2.join(); 47 | } 48 | -------------------------------------------------------------------------------- /chapter_22_condition_variables/condition.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std::literals; 8 | 9 | // Global variables 10 | std::mutex mut; 11 | std::condition_variable cv; 12 | std::string sdata{"Empty"}; 13 | 14 | // Waiting thread 15 | void reader() 16 | { 17 | // Acquire lock 18 | std::unique_lock guard(mut); 19 | 20 | // Unlock the mutex and 21 | // wait for condition variable to be notified 22 | cv.wait(guard); 23 | 24 | // Wake up and use the new value 25 | // The mutex is now locked again 26 | std::cout << "Data is " << sdata << "\n"; 27 | } 28 | 29 | // Modifying thread 30 | void writer() 31 | { 32 | std::cout << "Writing data...\n"; 33 | { 34 | // Lock the mutex 35 | std::lock_guard lg(mut); 36 | 37 | // Pretend to be busy... 38 | std::this_thread::sleep_for(2s); 39 | 40 | // Modify the data 41 | sdata = "Populated"; 42 | } 43 | 44 | // Notify the condition variable 45 | // The mutex is now unlocked 46 | cv.notify_one(); 47 | } 48 | 49 | int main() 50 | { 51 | std::cout << "Data is " << sdata << "\n"; 52 | std::thread read{reader}; 53 | std::thread write{writer}; 54 | write.join(); 55 | read.join(); 56 | } 57 | -------------------------------------------------------------------------------- /chapter_23_condition_variables_and_predicates/condition_predicate.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std::literals; 8 | 9 | // Global variables 10 | std::mutex mut; 11 | std::condition_variable cv; 12 | std::string sdata{"Empty"}; 13 | 14 | // Flag for predicate 15 | bool condition{false}; 16 | 17 | // Waiting thread 18 | void reader() 19 | { 20 | // Acquire lock 21 | std::unique_lock guard(mut); 22 | 23 | // Unlock the mutex and 24 | // wait for condition variable to be notified 25 | cv.wait(guard, []() { return condition; }); 26 | 27 | // Wake up and use the new value 28 | // The mutex is now locked again 29 | std::cout << "Data is " << sdata << "\n"; 30 | } 31 | 32 | // Modyifing thread 33 | void writer() 34 | { 35 | std::cout << "Writing data...\n"; 36 | { 37 | // Acquire lock 38 | std::lock_guard lg(mut); 39 | 40 | // Pretend to be busy... 41 | std::this_thread::sleep_for(2s); 42 | 43 | // Modify the data 44 | sdata = "Populated"; 45 | condition = true; 46 | } 47 | 48 | // Notify the condition variable 49 | // The mutex is now unlocked 50 | cv.notify_one(); 51 | } 52 | 53 | int main() 54 | { 55 | std::cout << "Data is " << sdata << "\n"; 56 | std::thread read{reader}; 57 | std::thread write{writer}; 58 | write.join(); 59 | read.join(); 60 | } 61 | -------------------------------------------------------------------------------- /chapter_24_futures_and_promises/future_promise.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // Producer function with std::promise argument 9 | void produce(std::promise& px) 10 | { 11 | int x{42}; 12 | std::this_thread::sleep_for(1s); 13 | std::cout << "The promise sets the shared state to " 14 | << x << "\n"; 15 | // Set the result 16 | px.set_value(x); 17 | } 18 | 19 | // Consumer function with std::future argument 20 | void consume(std::future& fx) 21 | { 22 | std::cout << "The future calls get()..." << "\n"; 23 | // Get the result 24 | int x = fx.get(); 25 | std::cout << "The future returns from get()\n"; 26 | std::cout << "The answer is " << x << "\n"; 27 | } 28 | 29 | int main() 30 | { 31 | // Create the promise object 32 | std::promise p; 33 | 34 | // Obtain the associated future 35 | std::future f = p.get_future(); 36 | 37 | // Start the consumer thread 38 | // Pass the future to it 39 | std::thread fut{consume, std::ref(f)}; 40 | 41 | // Start the producer thread 42 | // Pass the promise to it 43 | std::thread prom{produce, std::ref(p)}; 44 | 45 | fut.join(); 46 | prom.join(); 47 | } 48 | -------------------------------------------------------------------------------- /chapter_24_futures_and_promises/promise_except.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std::literals; 8 | 9 | // Producer function with std::promise argument 10 | void produce(std::promise& px) 11 | { 12 | try { 13 | int x{42}; 14 | std::this_thread::sleep_for(1s); 15 | throw std::out_of_range("Oops"); 16 | std::cout << "The promise sets the shared state" 17 | << " to " << x << "\n"; 18 | // Set the result 19 | px.set_value(x); 20 | } 21 | catch(...) { 22 | px.set_exception(std::current_exception()); 23 | } 24 | } 25 | 26 | // Consumer function with std::future argument 27 | void consume(std::future& fx) 28 | { 29 | std::cout << "The future calls get()...\n"; 30 | try { 31 | // Get the result 32 | int x = fx.get(); 33 | std::cout << "The future returns from get()\n"; 34 | std::cout << "The answer is " << x << "\n"; 35 | } 36 | catch(std::exception& e) { 37 | std::cout << "Exception caught: " 38 | << e.what() << "\n"; 39 | } 40 | } 41 | 42 | int main() 43 | { 44 | // Create the promise object 45 | std::promise p; 46 | 47 | // Obtain the associated future 48 | std::future f = p.get_future(); 49 | 50 | // Start the consumer thread 51 | // Pass the future to it 52 | std::thread fut{consume, std::ref(f)}; 53 | 54 | // Start the producer thread 55 | // Pass the promise to it 56 | std::thread prom{produce, std::ref(p)}; 57 | 58 | fut.join(); 59 | prom.join(); 60 | } 61 | -------------------------------------------------------------------------------- /chapter_25_shared_futures/promise_shared_future.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // Producer function with std::promise argument 9 | void produce(std::promise& px) 10 | { 11 | int x{42}; 12 | std::this_thread::sleep_for(1s); 13 | std::cout << "The promise sets the shared state to " 14 | << x << "\n"; 15 | // Set the result 16 | px.set_value(x); 17 | } 18 | 19 | // Consumer function with std::future argument 20 | void consume(std::shared_future& fx) 21 | { 22 | std::cout << "The future calls get()..." << "\n"; 23 | // Get the result 24 | int x = fx.get(); 25 | std::cout << "The future returns from get()\n"; 26 | std::cout << "The answer is " << x << "\n"; 27 | } 28 | 29 | int main() 30 | { 31 | std::promise p; 32 | 33 | // Move the promise's future into a shared future 34 | std::shared_future sf1{p.get_future()}; 35 | 36 | // Make a copy for the second consumer thread 37 | std::shared_future sf2 = sf1; 38 | 39 | // Start two consumer threads 40 | // Each has its own copy of the shared future 41 | std::thread fut1{consume, std::ref(sf1)}; 42 | std::thread fut2{consume, std::ref(sf2)}; 43 | 44 | // Start the producer thread 45 | // Pass the promise to it 46 | std::thread prom{produce, std::ref(p)}; 47 | 48 | fut1.join(); 49 | fut2.join(); 50 | prom.join(); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /chapter_26_atomic_operations/count.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Shared integer 6 | int counter {0}; 7 | 8 | // Thread entry point 9 | void task() 10 | { 11 | // Do we need a lock here? 12 | for (int i = 0; i < 100'000; ++i) 13 | ++counter; 14 | } 15 | 16 | int main() 17 | { 18 | std::vector tasks; 19 | for (int i = 0; i < 10; ++i) 20 | tasks.push_back(std::thread{task}); 21 | for (auto& t: tasks) 22 | t.join(); 23 | std::cout << counter << "\n"; 24 | } 25 | -------------------------------------------------------------------------------- /chapter_27_atomic_types/atomic_count.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Shared integer 7 | std::atomic counter {0}; 8 | 9 | // Thread entry point 10 | void task() 11 | { 12 | // Do we need a lock here? 13 | for (int i = 0; i < 100'000; ++i) 14 | ++counter; 15 | } 16 | 17 | int main() 18 | { 19 | std::vector tasks; 20 | for (int i = 0; i < 10; ++i) 21 | tasks.push_back(std::thread{task}); 22 | for (auto& t: tasks) 23 | t.join(); 24 | std::cout << counter << "\n"; 25 | } 26 | -------------------------------------------------------------------------------- /chapter_27_atomic_types/volatile_count.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Shared integer 6 | volatile int counter {0}; 7 | 8 | // Thread entry point 9 | void task() 10 | { 11 | // Do we need a lock here? 12 | for (int i = 0; i < 100'000; ++i) 13 | ++counter; 14 | } 15 | 16 | int main() 17 | { 18 | std::vector tasks; 19 | for (int i = 0; i < 10; ++i) 20 | tasks.push_back(std::thread{task}); 21 | for (auto& t: tasks) 22 | t.join(); 23 | std::cout << counter << "\n"; 24 | } 25 | -------------------------------------------------------------------------------- /chapter_29_spin_locks/spin.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | // Use atomic_flag as a simple lock 9 | std::atomic_flag lock_cout = ATOMIC_FLAG_INIT; // Initialize flag to false 10 | 11 | void task(int n) 12 | { 13 | while (lock_cout.test_and_set()) {} // "Lock" the flag 14 | std::this_thread::sleep_for(50ms); // Pretend to do some work 15 | std::cout << "I'm a task with argument " << n << "\n"; 16 | lock_cout.clear(); // "Unlock" the flag 17 | } 18 | 19 | int main() 20 | { 21 | std::vector threads; 22 | for (int i = 1; i <= 10; ++i) 23 | threads.push_back(std::thread{task, i}); 24 | for (auto& th : threads) 25 | th.join(); 26 | } 27 | -------------------------------------------------------------------------------- /chapter_30_lock_free_programming/singleton_atomic.cc: -------------------------------------------------------------------------------- 1 | #include "singleton_atomic.h" 2 | #include 3 | #include 4 | 5 | // Define the static member variable 6 | std::atomic Singleton::single{nullptr}; 7 | 8 | // Thread entry point function 9 | void task() 10 | { 11 | // Get the Singleton object 12 | Singleton* s = Singleton::get_singleton(); 13 | 14 | // Display its address 15 | std::cout << s << "\n"; 16 | } 17 | 18 | int main() 19 | { 20 | std::vector threads; 21 | for (int i = 0; i < 10; ++i) 22 | threads.push_back(std::thread{task}); 23 | for (auto& t : threads) 24 | t.join(); 25 | } 26 | -------------------------------------------------------------------------------- /chapter_30_lock_free_programming/singleton_atomic.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLETON_ATOMIC_H 2 | #define SINGLETON_ATOMIC_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | std::mutex mut; 9 | 10 | class Singleton { 11 | // Private static member 12 | static std::atomic single; 13 | public: 14 | // Static member function which 15 | // returns a Singleton object 16 | // This is the only way to get a Singleton 17 | static Singleton* get_singleton() 18 | { 19 | // If the member object does not already exist 20 | if (single == nullptr) { 21 | std::lock_guard lg(mut); 22 | // If the member object does not already exist 23 | if (single == nullptr) { 24 | // Create it 25 | single = new Singleton; 26 | } 27 | } 28 | return single; 29 | } 30 | 31 | protected: 32 | // The constructor is protected and 33 | // can only be called by a static member function 34 | Singleton() 35 | { 36 | std::cout << "Initializing singleton\n"; 37 | } 38 | }; 39 | 40 | #endif //SINGLETON_ATOMIC_H 41 | -------------------------------------------------------------------------------- /chapter_32_packaged_tasks/packaged_task.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | // Create a packaged task. The task function 7 | // is a lambda expression which takes 8 | // two ints and returns an int 9 | std::packaged_task pkg{ 10 | [](int a, int b) { return a + b; }}; 11 | 12 | // The packaged task has an associated promise 13 | // Get the corresponding future for the promise 14 | // Use the task function's return type 15 | // for the type parameter 16 | std::future fut = pkg.get_future(); 17 | 18 | // Create an std::thread to run the task 19 | // Move the packaged_task into the constructor 20 | std::thread t{std::move(pkg), 6, 7}; 21 | 22 | // Get the result of the task 23 | std::cout << "The result is: " << fut.get() << "\n"; 24 | t.join(); 25 | } 26 | -------------------------------------------------------------------------------- /chapter_33_the_std_async_function/async_exception.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std::literals; 5 | 6 | int func() 7 | { 8 | int x{42}; 9 | std::this_thread::sleep_for(2s); 10 | throw std::out_of_range("Oops"); 11 | return x; 12 | } 13 | 14 | int main() 15 | { 16 | auto f = std::async(func); 17 | 18 | try { 19 | auto result = f.get(); 20 | std::cout << "Result: " << result << "\n"; 21 | } 22 | catch (std::exception& e) { 23 | std::cout << "Caught exception: " << e.what() << "\n"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chapter_33_the_std_async_function/async_hello.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // The task function 5 | void hello() 6 | { 7 | std::cout << "Hello, Async!\n"; 8 | } 9 | 10 | int main() 11 | { 12 | // Call std::async() 13 | // Pass the task function as argument 14 | std::async(hello); 15 | } 16 | -------------------------------------------------------------------------------- /chapter_33_the_std_async_function/async_launch.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std::literals; 6 | 7 | // The task function 8 | int func() 9 | { 10 | std::cout << "Running in thread with ID " 11 | << std::this_thread::get_id() << "\n"; 12 | std::this_thread::sleep_for(2s); 13 | return 42; 14 | } 15 | 16 | int main() 17 | { 18 | std::cout << "Main thread with ID " 19 | << std::this_thread::get_id() << "\n"; 20 | std::cout << "Starting task\n"; 21 | 22 | // The task will be run in a separate thread 23 | auto result = std::async(std::launch::async, func); 24 | 25 | // The task will be run on this thread 26 | //auto result = std::async(std::launch::deferred, func); 27 | 28 | // The implememtation chooses how to run the task 29 | //auto result = std::async(func); 30 | 31 | // The implememtation chooses how to run the task 32 | //auto result = std::async( 33 | // std::launch::async|std::launch::deferred, 34 | // func); 35 | 36 | std::this_thread::sleep_for(1s); 37 | std::cout << "Calling get()\n"; 38 | std::cout << result.get() << "\n"; 39 | } 40 | -------------------------------------------------------------------------------- /chapter_37_execution_policies/non_policy_execution.cc: -------------------------------------------------------------------------------- 1 | // Requires Visual C++ compiler or g++ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std::execution; 8 | 9 | int main() 10 | { 11 | std::vector vec(2'000); 12 | int count{0}; 13 | std::for_each(par, begin(vec), end(vec), 14 | [&](int& x) { x = ++count; }); 15 | } 16 | -------------------------------------------------------------------------------- /chapter_37_execution_policies/parallel_execution_policy.cc: -------------------------------------------------------------------------------- 1 | // Requires Visual C++ compiler or g++ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std::execution; 8 | 9 | int main() 10 | { 11 | std::vector vec(2000); 12 | int count {0}; 13 | std::for_each(par, begin(vec), end(vec), [&] (int& x) { x = ++count; }); 14 | } 15 | -------------------------------------------------------------------------------- /chapter_37_execution_policies/parallel_unsequenced_execution_policy.cc: -------------------------------------------------------------------------------- 1 | // Requires Visual C++ compiler or g++ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std::execution; 8 | 9 | int main() 10 | { 11 | std::vector vec(2000); 12 | int count {0}; 13 | std::for_each(par_unseq, begin(vec), end(vec), [&] (int& x) { x = ++count; }); 14 | } 15 | -------------------------------------------------------------------------------- /chapter_37_execution_policies/sequenced_execution_policy.cc: -------------------------------------------------------------------------------- 1 | // Requires Visual C++ compiler or g++ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std::execution; 8 | 9 | int main() { 10 | std::vector vec(2000); 11 | int count {0}; 12 | std::for_each(seq, begin(vec), end(vec), [&] (int& x) { x = ++count; }); 13 | } 14 | -------------------------------------------------------------------------------- /chapter_38_algorithms_and_execution_policies/except_policy.cc: -------------------------------------------------------------------------------- 1 | // Requires Visual C++ compiler or g++ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std::execution; 9 | 10 | int main() 11 | { 12 | std::vector vec{3,1,4,1,5,9}; 13 | try { 14 | // Call the std::sort() algorithm 15 | // with a predicate that throws an exception 16 | std::sort(seq, begin(vec), end(vec), [](int a, int b) { 17 | throw std::out_of_range("Oops"); return true;} 18 | ); 19 | } 20 | catch (std::exception& e) { 21 | std::cout << "Caught exception: " << e.what() << "\n"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chapter_39_parallel_algorithms/transform_reduce.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Requires Visual C++ compiler or g++ 7 | //#include 8 | //using namespace std::execution; 9 | 10 | int main() 11 | { 12 | // The predicted results 13 | std::vector expected{0.1, 0.2, 0.3, 0.4, 0.5}; 14 | 15 | // The results of the experiment 16 | std::vector actual{0.09, 0.22, 0.27, 0.41, 0.52}; 17 | 18 | auto max_err = std::transform_reduce( 19 | //par_unseq, // Requires Visual C++ compiler or g++ 20 | begin(expected), end(expected), 21 | 22 | begin(actual), 23 | // Initial maximum error 24 | 0.0, 25 | // The reduce operation finds the biggest difference 26 | [](auto a, auto b) { return std::max(a, b); }, 27 | // The transform operation finds each difference 28 | [](auto l, auto r) { return std::fabs(l - r); }); 29 | std::cout << "The largest error is " << max_err << "\n"; 30 | } 31 | -------------------------------------------------------------------------------- /chapter_40_concurrent_queue_implementation/concurrent_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef CONCURRENT_QUEUE_H 2 | #define CONCURRENT_QUEUE_H 3 | 4 | #include 5 | #include 6 | 7 | // Define a namespace for the concurrent queue 8 | namespace cq { 9 | 10 | // Exception to throw when popping an empty queue 11 | class concurrent_queue_exception : public std::runtime_error { 12 | public: 13 | concurrent_queue_exception() : std::runtime_error("Queue is empty") {} 14 | concurrent_queue_exception(const char *s) : std::runtime_error(s) {} 15 | }; 16 | 17 | // The concurrent queue class 18 | template 19 | class concurrent_queue { 20 | // The mutex to protect the queue 21 | std::mutex m; 22 | // The wrapped queue object 23 | std::queue q; 24 | public: 25 | // Use defaults for the special member functions 26 | concurrent_queue() = default; 27 | 28 | // Thread-safe call to std::queue::push() 29 | void push(T value) 30 | { 31 | std::lock_guard lg(m); 32 | q.push(std::forward(value)); 33 | } 34 | 35 | void pop(T& value) 36 | { 37 | // Lock the mutex 38 | std::lock_guard lg(m); 39 | 40 | // Do not pop() an empty queue! 41 | if (q.empty()) { 42 | throw concurrent_queue_exception("Queue is empty"); 43 | } 44 | 45 | // Save the front element's value 46 | value = q.front(); 47 | 48 | // Remove the front element 49 | q.pop(); 50 | } 51 | }; 52 | } // End of namespace cq 53 | 54 | #endif //CONCURRENT_QUEUE_H 55 | -------------------------------------------------------------------------------- /chapter_40_concurrent_queue_implementation/concurrent_queue_cv.h: -------------------------------------------------------------------------------- 1 | #ifndef CONCURRENT_QUEUE_H 2 | #define CONCURRENT_QUEUE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // Define a namespace for the concurrent queue 9 | namespace cq { 10 | 11 | // Exception to throw when popping an empty queue 12 | class concurrent_queue_exception : public std::runtime_error { 13 | public: 14 | concurrent_queue_exception() : std::runtime_error("Queue is empty") {} 15 | concurrent_queue_exception(const char *s) : std::runtime_error(s) {} 16 | }; 17 | 18 | // The concurrent queue class 19 | template 20 | class concurrent_queue { 21 | // The mutex to protect the queue 22 | std::mutex m; 23 | // The wrapped queue object 24 | std::queue q; 25 | // The condition variable to signal new data 26 | std::condition_variable cv; 27 | public: 28 | // Use defaults for the "Rule of Five" 29 | concurrent_queue() = default; 30 | 31 | // Thread-safe call to std::queue::push() 32 | void push(T value) { 33 | std::lock_guard lg(m); 34 | q.push(value); 35 | cv.notify_one(); 36 | } 37 | 38 | void pop(T& value) { 39 | // Lock the mutex 40 | std::unique_lock lg(m); 41 | 42 | // Do not pop() an empty queue! 43 | cv.wait(lg, [this]() { return !q.empty(); }); 44 | 45 | // Save the front element's value 46 | value = q.front(); 47 | 48 | // Remove the front element 49 | q.pop(); 50 | } 51 | }; 52 | } // End of namespace cq 53 | 54 | #endif //CONCURRENT_QUEUE_H 55 | -------------------------------------------------------------------------------- /chapter_40_concurrent_queue_implementation/concurrent_queue_cv_main.cc: -------------------------------------------------------------------------------- 1 | #include "concurrent_queue_cv.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std::literals; 9 | 10 | // Create a concurrent queue object 11 | cq::concurrent_queue conc_queue; 12 | 13 | // Waiting thread 14 | void reader() { 15 | std::string sdata; 16 | // Pretend to be busy... 17 | std::this_thread::sleep_for(2s); 18 | 19 | std::cout << "Reader calling pop...\n"; 20 | 21 | // Pop the data off the queue 22 | conc_queue.pop(sdata); 23 | std::cout << "Reader received data: " << sdata << "\n"; 24 | } 25 | 26 | // Modyifing thread 27 | void writer() { 28 | std::cout << "Writer calling push...\n"; 29 | 30 | // Push the data onto the queue 31 | conc_queue.push("Populated"); 32 | std::cout << "Writer returned from push...\n"; 33 | } 34 | 35 | int main() { 36 | auto w = std::async(std::launch::async, writer); 37 | auto r = std::async(std::launch::async, reader); 38 | r.wait(); 39 | w.wait(); 40 | } 41 | -------------------------------------------------------------------------------- /chapter_40_concurrent_queue_implementation/concurrent_queue_main.cc: -------------------------------------------------------------------------------- 1 | #include "concurrent_queue.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std::literals; 9 | 10 | // Create a concurrent queue object 11 | cq::concurrent_queue conc_queue; 12 | 13 | // Waiting thread 14 | void reader() 15 | { 16 | std::string sdata; 17 | // Pretend to be busy... 18 | std::this_thread::sleep_for(2s); 19 | try { 20 | std::cout << "Reader calling pop...\n"; 21 | 22 | // Pop the data off the queue 23 | conc_queue.pop(sdata); 24 | std::cout << "Reader received data: " << sdata << "\n"; 25 | } 26 | catch (std::exception& e) { 27 | std::cout << "Exception caught: " << e.what() << "\n"; 28 | } 29 | } 30 | 31 | // Modyifing thread 32 | void writer() 33 | { 34 | std::cout << "Writer calling push...\n"; 35 | 36 | // Push the data onto the queue 37 | conc_queue.push("Populated"); 38 | std::cout << "Writer returned from push...\n"; 39 | } 40 | 41 | int main() 42 | { 43 | auto w = std::async(std::launch::async, writer); 44 | auto r = std::async(std::launch::async, reader); 45 | r.wait(); 46 | w.wait(); 47 | } 48 | -------------------------------------------------------------------------------- /chapter_42_thread_pool_implementation/thread_pool.cc: -------------------------------------------------------------------------------- 1 | #include "thread_pool.h" 2 | 3 | // Thread entry point function 4 | void thread_pool::worker() { 5 | // Infinite loop 6 | while (true) { 7 | Func task; 8 | 9 | // Get the next task from the queue 10 | work_queue.pop(task); 11 | 12 | // Call the task function 13 | task(); 14 | } 15 | } 16 | 17 | // Thread pool constructor 18 | thread_pool::thread_pool() { 19 | // Find the number of hardware threads 20 | const unsigned thread_count = std::thread::hardware_concurrency(); 21 | 22 | // Create that number of std::thread objects 23 | // Use worker() as the entry point 24 | // Populate the container 25 | for (unsigned i = 0; i < thread_count; ++i) 26 | threads.push_back(std::thread{&thread_pool::worker, this}); 27 | } 28 | 29 | // Destructor 30 | thread_pool::~thread_pool() { 31 | // Join all the threads in the container 32 | for (auto& t: threads) { 33 | t.join(); 34 | } 35 | } 36 | 37 | // Function for clients to submit tasks 38 | void thread_pool::submit(Func f) { 39 | work_queue.push(f); 40 | } 41 | -------------------------------------------------------------------------------- /chapter_42_thread_pool_implementation/thread_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef THREAD_POOL_H 2 | #define THREAD_POOL_H 3 | 4 | #include "concurrent_queue_cv.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Type alias to simplify the code 11 | using Func = std::function; 12 | 13 | class thread_pool { 14 | // The concurrent queue of task functions 15 | cq::concurrent_queue work_queue; 16 | 17 | // The container of threads 18 | std::vector threads; 19 | 20 | // The entry point function for the threads 21 | void worker(); 22 | public: 23 | thread_pool(); 24 | ~thread_pool(); 25 | 26 | // Member function to push a task on the queue 27 | void submit(Func f); 28 | }; 29 | 30 | #endif //THREAD_POOL_H 31 | -------------------------------------------------------------------------------- /chapter_42_thread_pool_implementation/thread_pool_main.cc: -------------------------------------------------------------------------------- 1 | #include "thread_pool.h" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std::literals; 7 | 8 | std::mutex m; 9 | 10 | void task() { 11 | std::lock_guard lg(m); 12 | std::cout << "Thread id: " 13 | << std::this_thread::get_id() << " started" << std::endl; 14 | std::this_thread::sleep_for(100ms); 15 | std::cout << "Thread id: " 16 | << std::this_thread::get_id() << " finished" << std::endl; 17 | } 18 | 19 | int main() { 20 | std::cout << "Creating a thread pool with " 21 | << std::thread::hardware_concurrency() 22 | << " threads" << std::endl; 23 | thread_pool pool; 24 | 25 | for (int i = 0; i < 20; ++i) 26 | pool.submit(task); 27 | 28 | std::this_thread::sleep_for(5s); 29 | } 30 | --------------------------------------------------------------------------------