commit 5da509e3732cb602f0fc1b475fc28bae6e2350cf Author: Christopher Hamer Date: Fri Jul 5 17:14:16 2024 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff9047e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/cmake-build-debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..569dbb8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.28) +project(task1) + +set(CMAKE_CXX_STANDARD 20) + +add_executable(task1 src/threadpool/main.cpp + src/threadpool/counter.cpp + include/counter.h + src/threadpool/sum.cpp + include/sum.h + src/threadpool/threadPool.cpp + include/threadPool.h) + +add_executable(task1_async src/async.cpp + include/async.h + ) +add_executable(sync src/sync.cpp include/sync.h) diff --git a/include/async.h b/include/async.h new file mode 100644 index 0000000..7c8337f --- /dev/null +++ b/include/async.h @@ -0,0 +1,14 @@ +// +// Created by Christopher Hamer on 02/07/2024. +// + +#ifndef TASK1_ASYNC_H +#define TASK1_ASYNC_H + + +class async { + +}; + + +#endif //TASK1_ASYNC_H diff --git a/include/counter.h b/include/counter.h new file mode 100644 index 0000000..7a089ec --- /dev/null +++ b/include/counter.h @@ -0,0 +1,20 @@ +// +// Created by Christopher Hamer on 19/06/2024. +// + +#include + +#ifndef TASK1_COUNTER_H +#define TASK1_COUNTER_H + + +class Counter { +public: + Counter(); + int getNext(); +private: + std::atomic value; +}; + + +#endif //TASK1_COUNTER_H diff --git a/include/sum.h b/include/sum.h new file mode 100644 index 0000000..2d58477 --- /dev/null +++ b/include/sum.h @@ -0,0 +1,24 @@ +// +// Created by Christopher Hamer on 19/06/2024. +// + +#include + +#ifndef TASK1_SUM_H +#define TASK1_SUM_H + + +// Thread-safe sum accumulator +class SumAccumulator { +public: + void add(int value); + + long long getTotal(); + +private: + long long sum = 0; + std::mutex mtx; +}; + + +#endif //TASK1_SUM_H diff --git a/include/sync.h b/include/sync.h new file mode 100644 index 0000000..1914e76 --- /dev/null +++ b/include/sync.h @@ -0,0 +1,14 @@ +// +// Created by Christopher Hamer on 02/07/2024. +// + +#ifndef TASK1_SYNC_H +#define TASK1_SYNC_H + + +class sync { + +}; + + +#endif //TASK1_SYNC_H diff --git a/include/threadPool.h b/include/threadPool.h new file mode 100644 index 0000000..553aba0 --- /dev/null +++ b/include/threadPool.h @@ -0,0 +1,50 @@ +// +// Created by Christopher Hamer on 19/06/2024. +// + +#ifndef TASK1_THREADPOOL_H +#define TASK1_THREADPOOL_H + + +#include +#include +#include +#include +#include +#include +using namespace std; + +// Class that represents a simple thread pool +class ThreadPool { +public: + // // Constructor to creates a thread pool with given + // number of threads + ThreadPool(size_t num_threads); + + // Destructor to stop the thread pool + ~ThreadPool(); + + // Enqueue task for execution by the thread pool + void enqueue(function task); + +private: + // Vector to store worker threads + vector threads_; + + // Queue of tasks + queue > tasks_; + + // Mutex to synchronize access to shared data + mutex queue_mutex_; + + // Condition variable to signal changes in the state of + // the tasks queue + condition_variable cv_; + + // Flag to indicate whether the thread pool should stop + // or not + bool stop_ = false; +}; + + +#endif //TASK1_THREADPOOL_H diff --git a/src/async.cpp b/src/async.cpp new file mode 100644 index 0000000..d25b99f --- /dev/null +++ b/src/async.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include +#include + +// Thread-safe counter class +class Counter { +private: + std::atomic value; +public: + Counter() : value(0) {} + int next() { + return ++value; + } +}; + +// Thread-safe sum class +class SumAccumulator { +private: + std::atomic total; +public: + SumAccumulator() : total(0) {} + void add(int value) { + total.fetch_add(value, std::memory_order_relaxed); + } + long long getTotal() const { + return total.load(std::memory_order_relaxed); + } +}; + +// Service function that reads the counter value, sleeps, and returns the value +int service(Counter& counter) { + int value = counter.next(); + // Sleep for a random amount of time up to 1 second + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(1, 1000); + std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen))); + return value; +} + +int main() { + Counter counter; + SumAccumulator sumAccumulator; + + const int totalCalls = 1000; // 1 million times + + // Vector to hold future objects + std::vector> futures; + + auto startTime = std::chrono::high_resolution_clock::now(); + + // Launching async tasks + for (int i = 0; i < totalCalls; ++i) { + futures.push_back(std::async(std::launch::async, [&counter, &sumAccumulator] { + int value = service(counter); + sumAccumulator.add(value); + })); + } + + // Wait for all tasks to complete + for (auto& fut : futures) { + fut.get(); + } + + auto endTime = std::chrono::high_resolution_clock::now(); + std::chrono::duration duration = endTime - startTime; + + // Final total should be 500,000,500,000 + std::cout << "Final Total: " << sumAccumulator.getTotal() << std::endl; + std::cout << "Execution Time: " << duration.count() << " seconds" << std::endl; + + return 0; +} diff --git a/src/sync.cpp b/src/sync.cpp new file mode 100644 index 0000000..5eb4696 --- /dev/null +++ b/src/sync.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include "thread" + +// Thread-safe counter class +class Counter { +private: + int value; +public: + Counter() : value(0) {} + int next() { + return ++value; + } +}; + +// Thread-safe sum class +class SumAccumulator { +private: + long total; +public: + SumAccumulator() : total(0) {} + void add(int value) { + total += value; + } + long getTotal() const { + return total; + } +}; + +// Service function that reads the counter value, sleeps, and returns the value +int service(Counter& counter) { + int value = counter.next(); + // Sleep for a random amount of time up to 1 second + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(1, 1000); + std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen))); + return value; +} + +int main() { + Counter counter; + SumAccumulator sumAccumulator; + + const int totalCalls = 100; // 1 million times + + auto startTime = std::chrono::high_resolution_clock::now(); + + // Run the service synchronously + for (int i = 0; i < totalCalls; ++i) { + int value = service(counter); + sumAccumulator.add(value); + } + + auto endTime = std::chrono::high_resolution_clock::now(); + std::chrono::duration duration = endTime - startTime; + + // Final total should be 500,000,500,000 + std::cout << "Final Total: " << sumAccumulator.getTotal() << std::endl; + std::cout << "Execution Time: " << duration.count() << " seconds" << std::endl; + + return 0; +} diff --git a/src/threadpool/counter.cpp b/src/threadpool/counter.cpp new file mode 100644 index 0000000..f165a49 --- /dev/null +++ b/src/threadpool/counter.cpp @@ -0,0 +1,11 @@ +// +// Created by Christopher Hamer on 19/06/2024. +// + +#include "../../include/counter.h" + +Counter::Counter() : value(0) {} + +int Counter::getNext() { + return ++value; +} \ No newline at end of file diff --git a/src/threadpool/main.cpp b/src/threadpool/main.cpp new file mode 100644 index 0000000..c7eed2c --- /dev/null +++ b/src/threadpool/main.cpp @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../include/counter.h" +#include "../../include/sum.h" +#include "../../include/threadPool.h" + +// Simulated service function +int service(Counter& counter) { + int value = counter.getNext(); + + // Sleep for a random amount of time up to 1 second + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 1000); + std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen))); + + return value; +} + +int main() { + const int numCalls1 = 10000; + const int maxThreads1 = 100; + + Counter counter; + SumAccumulator sumAccumulator; + + auto worker = [&counter, &sumAccumulator]() { + int value = service(counter); + sumAccumulator.add(value); + }; + + ThreadPool pool(maxThreads1); + + std::cout << "Running service " << numCalls1 << " times with up to " << maxThreads1 << " threads..." << std::endl; + + auto startTime = std::chrono::high_resolution_clock::now(); + + // Enqueue tasks for execution + for (int i = 0; i < numCalls1; ++i) { + pool.enqueue([&worker] { + worker(); + }); + } + + // Wait for all tasks to complete + pool.~ThreadPool(); + + auto endTime = std::chrono::high_resolution_clock::now(); + std::chrono::duration duration = endTime - startTime; + + std::cout << "Final Total: " << sumAccumulator.getTotal() << std::endl; + std::cout << "Execution Time: " << duration.count() << " seconds" << std::endl; + + + return 0; +} diff --git a/src/threadpool/sum.cpp b/src/threadpool/sum.cpp new file mode 100644 index 0000000..1f3c1a6 --- /dev/null +++ b/src/threadpool/sum.cpp @@ -0,0 +1,16 @@ +// +// Created by Christopher Hamer on 19/06/2024. +// + +#include "../../include/sum.h" + + +void SumAccumulator::add(int value) { + std::lock_guard lock(mtx); + sum += value; +} + +long long SumAccumulator::getTotal() { + std::lock_guard lock(mtx); + return sum; +} diff --git a/src/threadpool/threadPool.cpp b/src/threadpool/threadPool.cpp new file mode 100644 index 0000000..57e9d4f --- /dev/null +++ b/src/threadpool/threadPool.cpp @@ -0,0 +1,71 @@ +// +// Created by Christopher Hamer on 19/06/2024. +// + +#include "../../include/threadPool.h" + +ThreadPool::ThreadPool(size_t num_threads) { + + // Creating worker threads + for (size_t i = 0; i < num_threads; ++i) { + threads_.emplace_back([this] { + while (true) { + function task; + // The reason for putting the below code + // here is to unlock the queue before + // executing the task so that other + // threads can perform enqueue tasks + { + // Locking the queue so that data + // can be shared safely + unique_lock lock( + queue_mutex_); + + // Waiting until there is a task to + // execute or the pool is stopped + cv_.wait(lock, [this] { + return !tasks_.empty() || stop_; + }); + + // exit the thread in case the pool + // is stopped and there are no tasks + if (stop_ && tasks_.empty()) { + return; + } + + // Get the next task from the queue + task = move(tasks_.front()); + tasks_.pop(); + } + + task(); + } + }); + } + +} + +ThreadPool::~ThreadPool() { + { + // Lock the queue to update the stop flag safely + unique_lock lock(queue_mutex_); + stop_ = true; + } + + // Notify all threads + cv_.notify_all(); + + // Joining all worker threads to ensure they have + // completed their tasks + for (auto& thread : threads_) { + thread.join(); + } +} + +void ThreadPool::enqueue(function task) { + { + unique_lock lock(queue_mutex_); + tasks_.emplace(move(task)); + } + cv_.notify_one(); +} \ No newline at end of file