initial commit

This commit is contained in:
Christopher Hamer 2024-07-05 17:14:16 +01:00
commit 5da509e373
13 changed files with 443 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/cmake-build-debug/

17
CMakeLists.txt Normal file
View File

@ -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)

14
include/async.h Normal file
View File

@ -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

20
include/counter.h Normal file
View File

@ -0,0 +1,20 @@
//
// Created by Christopher Hamer on 19/06/2024.
//
#include <mutex>
#ifndef TASK1_COUNTER_H
#define TASK1_COUNTER_H
class Counter {
public:
Counter();
int getNext();
private:
std::atomic<int> value;
};
#endif //TASK1_COUNTER_H

24
include/sum.h Normal file
View File

@ -0,0 +1,24 @@
//
// Created by Christopher Hamer on 19/06/2024.
//
#include <mutex>
#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

14
include/sync.h Normal file
View File

@ -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

50
include/threadPool.h Normal file
View File

@ -0,0 +1,50 @@
//
// Created by Christopher Hamer on 19/06/2024.
//
#ifndef TASK1_THREADPOOL_H
#define TASK1_THREADPOOL_H
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>
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<void()> task);
private:
// Vector to store worker threads
vector<thread> threads_;
// Queue of tasks
queue<function<void()> > 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

77
src/async.cpp Normal file
View File

@ -0,0 +1,77 @@
#include <iostream>
#include <atomic>
#include <thread>
#include <vector>
#include <chrono>
#include <random>
#include <future>
// Thread-safe counter class
class Counter {
private:
std::atomic<int> value;
public:
Counter() : value(0) {}
int next() {
return ++value;
}
};
// Thread-safe sum class
class SumAccumulator {
private:
std::atomic<long long> 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<std::future<void>> 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<double> 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;
}

65
src/sync.cpp Normal file
View File

@ -0,0 +1,65 @@
#include <iostream>
#include <atomic>
#include <chrono>
#include <random>
#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<double> 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;
}

View File

@ -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;
}

63
src/threadpool/main.cpp Normal file
View File

@ -0,0 +1,63 @@
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
#include <random>
#include <chrono>
#include <mutex>
#include <numeric>
#include <future>
#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<double> duration = endTime - startTime;
std::cout << "Final Total: " << sumAccumulator.getTotal() << std::endl;
std::cout << "Execution Time: " << duration.count() << " seconds" << std::endl;
return 0;
}

16
src/threadpool/sum.cpp Normal file
View File

@ -0,0 +1,16 @@
//
// Created by Christopher Hamer on 19/06/2024.
//
#include "../../include/sum.h"
void SumAccumulator::add(int value) {
std::lock_guard<std::mutex> lock(mtx);
sum += value;
}
long long SumAccumulator::getTotal() {
std::lock_guard<std::mutex> lock(mtx);
return sum;
}

View File

@ -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<void()> 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<mutex> 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<mutex> 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<void()> task) {
{
unique_lock<std::mutex> lock(queue_mutex_);
tasks_.emplace(move(task));
}
cv_.notify_one();
}