Compare commits
3 Commits
a97dfbe34b
...
097af8d222
Author | SHA1 | Date |
---|---|---|
Shaun Reed | 097af8d222 | |
Shaun Reed | d81c65b1d2 | |
Shaun Reed | fc1f247987 |
|
@ -18,10 +18,12 @@ project(
|
||||||
)
|
)
|
||||||
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
add_compile_options("-Wall")
|
||||||
|
|
||||||
add_subdirectory(algorithms)
|
add_subdirectory(algorithms)
|
||||||
add_subdirectory(cmake-example)
|
add_subdirectory(cmake-example)
|
||||||
add_subdirectory(cryptography)
|
add_subdirectory(cryptography)
|
||||||
add_subdirectory(datastructs)
|
add_subdirectory(datastructs)
|
||||||
add_subdirectory(graphics)
|
add_subdirectory(graphics)
|
||||||
|
add_subdirectory(multithreading)
|
||||||
add_subdirectory(patterns)
|
add_subdirectory(patterns)
|
|
@ -15,9 +15,9 @@
|
||||||
void BubbleSort(std::vector<int> &array)
|
void BubbleSort(std::vector<int> &array)
|
||||||
{
|
{
|
||||||
// For each value within the set, starting at 0
|
// For each value within the set, starting at 0
|
||||||
for (int sortedPivot = 0; sortedPivot < array.size(); sortedPivot++) {
|
for (size_t sortedPivot = 0; sortedPivot < array.size(); sortedPivot++) {
|
||||||
// Check every other remaining value in the set
|
// Check every other remaining value in the set
|
||||||
for (int j = array.size() - 1; j > sortedPivot; j--) {
|
for (size_t j = array.size() - 1; j > sortedPivot; j--) {
|
||||||
// Swap if the value at j is less than the value before it
|
// Swap if the value at j is less than the value before it
|
||||||
if (array[j] < array[j - 1]) {
|
if (array[j] < array[j - 1]) {
|
||||||
std::swap(array[j], array[j - 1]);
|
std::swap(array[j], array[j - 1]);
|
||||||
|
|
|
@ -33,7 +33,7 @@ void CountingSort(std::vector<int> &array)
|
||||||
|
|
||||||
// Count the values less than or equal to each element of tempArray
|
// Count the values less than or equal to each element of tempArray
|
||||||
// + Since each element stores its own count, just add the count at index i-1
|
// + Since each element stores its own count, just add the count at index i-1
|
||||||
for (size_t i = 1; i <= maxValue; i++) {
|
for (int32_t i = 1; i <= maxValue; i++) {
|
||||||
tempArray[i] += tempArray[i - 1];
|
tempArray[i] += tempArray[i - 1];
|
||||||
// tempArray[i] - 1 now represents the sorted 0-index pos for each value i
|
// tempArray[i] - 1 now represents the sorted 0-index pos for each value i
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ size_t Parent(const size_t &index) { return index / 2;}
|
||||||
size_t Left(const size_t &index) { return 2 * index + 1;}
|
size_t Left(const size_t &index) { return 2 * index + 1;}
|
||||||
size_t Right(const size_t &index) { return (2 * index) + 2;}
|
size_t Right(const size_t &index) { return (2 * index) + 2;}
|
||||||
|
|
||||||
void MaxHeapify(std::vector<int> &array, size_t thisIndex, const int &heapSize)
|
void MaxHeapify(std::vector<int> &array, size_t thisIndex, const size_t &heapSize)
|
||||||
{
|
{
|
||||||
// Get an index for the left and right nodes attached to thisIndex
|
// Get an index for the left and right nodes attached to thisIndex
|
||||||
size_t l = Left(thisIndex);
|
size_t l = Left(thisIndex);
|
||||||
|
|
|
@ -18,7 +18,7 @@ size_t Parent(const size_t &index);
|
||||||
size_t Left(const size_t &index);
|
size_t Left(const size_t &index);
|
||||||
size_t Right(const size_t &index);
|
size_t Right(const size_t &index);
|
||||||
|
|
||||||
void MaxHeapify(std::vector<int> &array, size_t thisIndex, const int &heapSize);
|
void MaxHeapify(std::vector<int> &array, size_t thisIndex, const size_t &heapSize);
|
||||||
|
|
||||||
void BuildMaxHeap(std::vector<int> &array);
|
void BuildMaxHeap(std::vector<int> &array);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ void InsertionSort(std::vector<int> &array)
|
||||||
{
|
{
|
||||||
// For each value, move left until we find sortedPosition for keyValue
|
// For each value, move left until we find sortedPosition for keyValue
|
||||||
// + Starting with keyValue at array[1], to check sortedPosition at array[0]
|
// + Starting with keyValue at array[1], to check sortedPosition at array[0]
|
||||||
for (int keyIndex = 1; keyIndex <= array.size(); keyIndex++) {
|
for (size_t keyIndex = 1; keyIndex <= array.size(); keyIndex++) {
|
||||||
// Save the current key value
|
// Save the current key value
|
||||||
// + We will look for the sorted position of this value
|
// + We will look for the sorted position of this value
|
||||||
const int keyValue = array[keyIndex];
|
const int keyValue = array[keyIndex];
|
||||||
|
|
|
@ -50,7 +50,7 @@ size_t Partition(std::vector<int> &array, size_t begin, size_t end)
|
||||||
// + Return this value when done, so we know where the lhs partition ends
|
// + Return this value when done, so we know where the lhs partition ends
|
||||||
ssize_t lhsIndex = begin - 1;
|
ssize_t lhsIndex = begin - 1;
|
||||||
// For each value within this partition, check for values < keyValue
|
// For each value within this partition, check for values < keyValue
|
||||||
for (int j = begin; j <= end - 1; j++) {
|
for (size_t j = begin; j <= end - 1; j++) {
|
||||||
if (array[j] <= keyValue) {
|
if (array[j] <= keyValue) {
|
||||||
// Swap all values < keyValue into the lhs portion of array
|
// Swap all values < keyValue into the lhs portion of array
|
||||||
std::swap(array[++lhsIndex], array[j]);
|
std::swap(array[++lhsIndex], array[j]);
|
||||||
|
|
|
@ -41,7 +41,7 @@ void CountingSort(std::vector<int> &array, int placeValue)
|
||||||
|
|
||||||
// Count the values less than or equal to each element of tempArray
|
// Count the values less than or equal to each element of tempArray
|
||||||
// + Since each element stores its own count, just add the count at index i-1
|
// + Since each element stores its own count, just add the count at index i-1
|
||||||
for (int i = 1; i < tempArray.size(); i++) {
|
for (size_t i = 1; i < tempArray.size(); i++) {
|
||||||
tempArray[i] = tempArray[i] + tempArray[i - 1];
|
tempArray[i] = tempArray[i] + tempArray[i - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,10 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
void SelectionSort(std::vector<int> &arr) {
|
void SelectionSort(std::vector<int> &arr) {
|
||||||
for (int leftIndex = 0; leftIndex < arr.size(); leftIndex++) {
|
for (size_t leftIndex = 0; leftIndex < arr.size(); leftIndex++) {
|
||||||
// Get the index for the minimum number in the unsorted set
|
// Get the index for the minimum number in the unsorted set
|
||||||
int min = leftIndex;
|
size_t min = leftIndex;
|
||||||
for (int i = leftIndex; i < arr.size(); i++) {
|
for (size_t i = leftIndex; i < arr.size(); i++) {
|
||||||
// Check if value at i is smaller than value at min index
|
// Check if value at i is smaller than value at min index
|
||||||
min = (arr[min] > arr[i]) ? i : min; // Update min value to i if true
|
min = (arr[min] > arr[i]) ? i : min; // Update min value to i if true
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,9 @@ void Columnar::InitOrder(std::string temp)
|
||||||
temp.erase(it, temp.end());
|
temp.erase(it, temp.end());
|
||||||
|
|
||||||
// Step through each character in lexicographic order
|
// Step through each character in lexicographic order
|
||||||
for (int i = 0; i < temp.size(); i++) {
|
for (size_t i = 0; i < temp.size(); i++) {
|
||||||
// Check each character in the keyWord for the current lexicographic char
|
// Check each character in the keyWord for the current lexicographic char
|
||||||
for (int j = 0; j < keyWord_.size(); j++) {
|
for (size_t j = 0; j < keyWord_.size(); j++) {
|
||||||
// If they are equal, push the index of the char in keyWord to orderVect
|
// If they are equal, push the index of the char in keyWord to orderVect
|
||||||
if (keyWord_[j] == temp[i]) {
|
if (keyWord_[j] == temp[i]) {
|
||||||
orderVect_.push_back(j);
|
orderVect_.push_back(j);
|
||||||
|
@ -109,7 +109,7 @@ std::string Columnar::Decrypt(std::string message)
|
||||||
rows.resize(orderVect_.size());
|
rows.resize(orderVect_.size());
|
||||||
// Track the ending position after each substring is taken
|
// Track the ending position after each substring is taken
|
||||||
int lastPos = 0;
|
int lastPos = 0;
|
||||||
for (int i = 0; i < orderVect_.size(); i++) {
|
for (size_t i = 0; i < orderVect_.size(); i++) {
|
||||||
// If we are assigning to any row < fullRows, it should have + 1 character
|
// If we are assigning to any row < fullRows, it should have + 1 character
|
||||||
if (orderVect_[i] < fullRows) {
|
if (orderVect_[i] < fullRows) {
|
||||||
rows[orderVect_[i]] = message.substr(lastPos, rowLength + 1);
|
rows[orderVect_[i]] = message.substr(lastPos, rowLength + 1);
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
################################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
||||||
|
## About: A root project for practicing C++ multithreading ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
project(
|
||||||
|
#[[NAME]] Multithreading
|
||||||
|
VERSION 1.0
|
||||||
|
DESCRIPTION "Practice with multithreaded programming in C++"
|
||||||
|
LANGUAGES CXX
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(deadlock)
|
||||||
|
add_subdirectory(race-condition)
|
|
@ -0,0 +1,26 @@
|
||||||
|
################################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
||||||
|
## About: An example and solution for deadlocks in C++ ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
# std::scoped_lock requires C++17
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
add_compile_options("-Wall")
|
||||||
|
|
||||||
|
project(
|
||||||
|
#[[NAME]] Deadlock
|
||||||
|
VERSION 1.0
|
||||||
|
DESCRIPTION "Example and solution for deadlocks in C++"
|
||||||
|
LANGUAGES CXX
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(
|
||||||
|
multithread-deadlock driver.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(multithread-deadlock pthread)
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
||||||
|
## About: An example and solution for deadlocks in C++ ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
################################################################################
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
static std::mutex mtx_A, mtx_B, output;
|
||||||
|
|
||||||
|
// Helper function to output thread ID and string associated with mutex name
|
||||||
|
// + This must also be thread-safe, since we want threads to produce output
|
||||||
|
// + There is no bug or issue here; This is just in support of example output
|
||||||
|
void print_safe(const std::string & s) {
|
||||||
|
std::scoped_lock<std::mutex> scopedLock(output);
|
||||||
|
std::cout << s << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to convert std::thread::id to string
|
||||||
|
std::string id_string(const std::thread::id & id) {
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << id;
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the two threads within this function, we have a problem
|
||||||
|
// + The mutex locks are acquired in reverse order, so they collide
|
||||||
|
// + This is called a deadlock; The program will *never* finish
|
||||||
|
void problem() {
|
||||||
|
std::thread thread_A([]()->void {
|
||||||
|
mtx_A.lock();
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + " thread_A: Locked A");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
mtx_B.lock(); // We can't lock B! thread_B is using it
|
||||||
|
// The program will never reach this point in execution; We are in deadlock
|
||||||
|
print_safe(id_string(std::this_thread::get_id())
|
||||||
|
+ " thread_A: B has been unlocked, we can proceed!\n Locked B"
|
||||||
|
);
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
print_safe(id_string(std::this_thread::get_id())
|
||||||
|
+ " thread_A: Unlocking A, B..."
|
||||||
|
);
|
||||||
|
mtx_A.unlock();
|
||||||
|
mtx_B.unlock();
|
||||||
|
});
|
||||||
|
|
||||||
|
std::thread thread_B([]()->void {
|
||||||
|
mtx_B.lock();
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + " thread_B: Locked B");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
mtx_A.lock(); // We can't lock A! thread_A is using it
|
||||||
|
// The program will never reach this point in execution; We are in deadlock
|
||||||
|
print_safe(id_string(std::this_thread::get_id())
|
||||||
|
+ " thread_B: A has been unlocked, we can proceed!\n Locked A"
|
||||||
|
);
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
print_safe(id_string(std::this_thread::get_id())
|
||||||
|
+ " thread_B: Unlocking B, A..."
|
||||||
|
);
|
||||||
|
mtx_B.unlock();
|
||||||
|
mtx_A.unlock();
|
||||||
|
});
|
||||||
|
|
||||||
|
// This offers a way out of the deadlock, so we can proceed to the solution
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
char input;
|
||||||
|
print_safe("\n"
|
||||||
|
+ id_string(std::this_thread::get_id())
|
||||||
|
+ " problem(): We are in a deadlock. \n"
|
||||||
|
+ " Enter y/Y to continue to the solution...\n"
|
||||||
|
);
|
||||||
|
while (std::cin >> input) {
|
||||||
|
if (input != 'Y' && input != 'y') continue;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
print_safe(id_string(std::this_thread::get_id())
|
||||||
|
+ " problem(): Unlocking A, B..."
|
||||||
|
);
|
||||||
|
mtx_A.unlock();
|
||||||
|
mtx_B.unlock();
|
||||||
|
|
||||||
|
thread_A.join();
|
||||||
|
thread_B.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::lock will lock N mutex locks
|
||||||
|
// + If either is in use, execution will block until both are available to lock
|
||||||
|
void solution_A() {
|
||||||
|
std::thread thread_A([]()->void {
|
||||||
|
std::lock(mtx_A, mtx_B);
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Locked A, B");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking A, B...");
|
||||||
|
mtx_A.unlock();
|
||||||
|
mtx_B.unlock();
|
||||||
|
});
|
||||||
|
|
||||||
|
std::thread thread_B([]()->void {
|
||||||
|
std::lock(mtx_B, mtx_A);
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Locked B, A");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking B, A...");
|
||||||
|
mtx_B.unlock();
|
||||||
|
mtx_A.unlock();
|
||||||
|
});
|
||||||
|
|
||||||
|
thread_A.join();
|
||||||
|
thread_B.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::lock_guard is a C++11 object which can be constructed with 1 mutex
|
||||||
|
// + When the program leaves the scope of the guard, the mutex is unlocked
|
||||||
|
void solution_B() {
|
||||||
|
std::thread thread_A([]()->void {
|
||||||
|
// lock_guard will handle unlocking when program leaves this scope
|
||||||
|
std::lock_guard<std::mutex> guard_A(mtx_A), guard_B(mtx_B);
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Locked A, B");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking A, B...");
|
||||||
|
// We don't need to explicitly unlock either mutex
|
||||||
|
});
|
||||||
|
|
||||||
|
std::thread thread_B([]()->void {
|
||||||
|
std::lock_guard<std::mutex> guard_B(mtx_B), guard_A(mtx_A);
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Locked B, A");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking B, A...");
|
||||||
|
// We don't need to explicitly unlock either mutex
|
||||||
|
});
|
||||||
|
|
||||||
|
thread_A.join();
|
||||||
|
thread_B.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::scoped_lock is a C++17 object that can be constructed with N mutex
|
||||||
|
// + When the program leaves this scope, all N mutex will be unlocked
|
||||||
|
void solution_C() {
|
||||||
|
std::thread thread_A([]()->void {
|
||||||
|
// scoped_lock will handle unlocking when program leaves this scope
|
||||||
|
std::scoped_lock scopedLock(mtx_A, mtx_B);
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Locked A, B");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking A, B...");
|
||||||
|
// We don't need to explicitly unlock either mutex
|
||||||
|
});
|
||||||
|
|
||||||
|
std::thread thread_B([]()->void {
|
||||||
|
std::scoped_lock scopedLock(mtx_A, mtx_B);
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Locked B, A");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
|
||||||
|
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking B, A...");
|
||||||
|
// We don't need to explicitly unlock either mutex
|
||||||
|
});
|
||||||
|
|
||||||
|
thread_A.join();
|
||||||
|
thread_B.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(const int argc, const char * argv[]) {
|
||||||
|
std::cout << "main() thread id: " << std::this_thread::get_id() << std::endl;
|
||||||
|
|
||||||
|
problem();
|
||||||
|
|
||||||
|
print_safe("\nsolution_A, using std::lock\n");
|
||||||
|
solution_A();
|
||||||
|
|
||||||
|
print_safe("\nsolution_B, using std::lock_guard\n");
|
||||||
|
solution_B();
|
||||||
|
|
||||||
|
print_safe("\nsolution_C, using std::scoped_lock\n");
|
||||||
|
solution_C();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
################################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
||||||
|
## About: An example and solution for race conditions in C++ ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
project(
|
||||||
|
#[[NAME]] RaceCondition
|
||||||
|
VERSION 1.0
|
||||||
|
DESCRIPTION "Example and solution for race conditions"
|
||||||
|
LANGUAGES CXX
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(
|
||||||
|
multithread-race-condition driver.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(multithread-race-condition pthread)
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*##############################################################################
|
||||||
|
## Author: Shaun Reed ##
|
||||||
|
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
|
||||||
|
## About: An example of a race condition problem and solution ##
|
||||||
|
## ##
|
||||||
|
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
|
||||||
|
################################################################################
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
void problem() {
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
const uint8_t thread_count = 5;
|
||||||
|
// With no mutex lock, the final value will vary in the range 1000000-5000000
|
||||||
|
// + Threads will modify x simultaneously, so some iterations will be lost
|
||||||
|
// + x will have same initial value entering this loop on different threads
|
||||||
|
uint32_t x = 0;
|
||||||
|
for (uint8_t i = 0; i < thread_count; i++) {
|
||||||
|
threads.emplace_back([&x](){
|
||||||
|
for (uint32_t i = 0; i < 1000000; i++) {
|
||||||
|
x = x + 1;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Ensure the function doesn't continue until all threads are finished
|
||||||
|
// + There's no issue here, the issue is in how `x` is accessed above
|
||||||
|
for (auto &thread : threads) thread.join();
|
||||||
|
std::cout << x << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create mutex lock to prevent threads from modifying same value simultaneously
|
||||||
|
static std::mutex mtx;
|
||||||
|
void solution() {
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
const uint8_t thread_count = 5;
|
||||||
|
uint32_t x = 0;
|
||||||
|
for (uint8_t i = 0; i < thread_count; i++) {
|
||||||
|
threads.emplace_back([&x](){
|
||||||
|
// The first thread that arrives here will 'lock' other threads from passing
|
||||||
|
// + Once first thread finishes, the next thread will resume
|
||||||
|
// + This process repeats until all threads finish
|
||||||
|
std::lock_guard<std::mutex> lock(mtx);
|
||||||
|
for (uint32_t i = 0; i < 1000000; i++) {
|
||||||
|
x = x + 1;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Ensure the function doesn't continue until all threads are finished
|
||||||
|
for (auto &thread : threads) thread.join();
|
||||||
|
std::cout << x << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(const int argc, const char * argv[]) {
|
||||||
|
// Result will vary from 1000000-5000000
|
||||||
|
problem();
|
||||||
|
|
||||||
|
// Result will always be 5000000
|
||||||
|
solution();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue