├── 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