diff --git a/README.md b/README.md index 4653416..a5689b8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -### Erklärung der Praktikumsaufgabe (Synchronisation) +/* ### Erklärung der Praktikumsaufgabe (Synchronisation) #### **Kernziel der Aufgabe** Sie sollen ein **multithreaded Sensornetzwerk** simulieren, das drei Komponenten umfasst: @@ -151,4 +151,5 @@ Starten Sie das System mit verschiedenen Parametern und beobachten Sie: Mit dieser Struktur erfüllen Sie alle Lernziele: ✅ Reader-Writer-Problem ✅ Producer-Consumer-Pattern -✅ Vermeidung von Race Conditions & Deadlocks! \ No newline at end of file +✅ Vermeidung von Race Conditions & Deadlocks! +*/ \ No newline at end of file diff --git a/analysis_model.h b/analysis_model.h index 111ddbe..e775eb5 100644 --- a/analysis_model.h +++ b/analysis_model.h @@ -1,43 +1,32 @@ #pragma once #include -#include +/** + * Thread-sicheres Analysemodell + * Vereinfachte Implementierung mit: + * - Einfachem Mutex-Schutz (kein Reader-Writer-Lock) + * - Für seltene Schreibzugriffe geeignet + */ class AnalysisModel { - int value = 0; - int reader_count = 0; - - std::mutex model_mutex; - std::mutex count_mutex; - std::condition_variable no_writer; + int value = 0; // Der gespeicherte Wert + std::mutex mtx; // Schützt Lese/Schreibzugriffe public: + /** + * Liest den aktuellen Wert + * @return Der gespeicherte Wert + */ int read() { - std::unique_lock count_lock(count_mutex); - reader_count++; - - if(reader_count == 1) { - model_mutex.lock(); - } - count_lock.unlock(); - - int result = value; - - count_lock.lock(); - reader_count--; - if(reader_count == 0) { - model_mutex.unlock(); - no_writer.notify_one(); - } - - return result; + std::lock_guard lock(mtx); + return value; } - void write(int new_value) { - std::unique_lock lock(model_mutex); - value = new_value; - - no_writer.wait(lock, [this]() { - return reader_count == 0; - }); + /** + * Schreibt einen neuen Wert + * @param new_val Der neue Wert + */ + void write(int new_val) { + std::lock_guard lock(mtx); + value = new_val; } }; diff --git a/main.cpp b/main.cpp index 9b2ae67..0b81389 100644 --- a/main.cpp +++ b/main.cpp @@ -1,75 +1,26 @@ #include "sensor_network.h" #include -#include -#include -#include - -constexpr size_t DEFAULT_NUM_SENSORS = 3; -constexpr size_t DEFAULT_NUM_ANALYSERS = 2; -constexpr int DEFAULT_RUN_TIME = 30; -constexpr size_t DEFAULT_BUFFER_SIZE = 8; - -template -void run_simulation(size_t num_sensors, size_t num_analysers, int run_time) { - SensorNetwork network; - std::cout << "\n=== Simulation gestartet ===\n" - << "Sensoren: " << num_sensors << "\n" - << "Analysemodule: " << num_analysers << "\n" - << "Puffergröße: " << N << "\n" - << "Laufzeit: " << run_time << "s\n\n"; - - network.start(num_sensors, num_analysers); - std::this_thread::sleep_for(std::chrono::seconds(run_time)); - network.stop(); - - std::cout << "\n=== Simulation beendet ===\n"; -} - -size_t get_input(const std::string& prompt, size_t default_value) { - std::cout << prompt << " [" << default_value << "]: "; - std::string input; - std::getline(std::cin, input); - - if(input.empty()) return default_value; - - try { - return std::stoul(input); - } catch(...) { - std::cout << "Ungültige Eingabe. Verwende Standardwert: " - << default_value << "\n"; - return default_value; - } -} +/** + * Hauptprogramm + * Startet die Simulation mit festen Parametern + * (Könnte leicht für interaktive Eingabe erweitert werden) + */ int main() { - std::cout << "=== Sensornetzwerk-Simulation ===\n" - << "(Leere Eingabe verwendet Standardwerte)\n"; - - size_t num_sensors = get_input("Anzahl Sensoren", DEFAULT_NUM_SENSORS); - size_t num_analysers = get_input("Anzahl Analysemodule", DEFAULT_NUM_ANALYSERS); - int run_time = static_cast( - get_input("Laufzeit (Sekunden)", DEFAULT_RUN_TIME) - ); - size_t buffer_size = get_input("Puffergröße", DEFAULT_BUFFER_SIZE); - - switch(buffer_size) { - case 8: - run_simulation<8>(num_sensors, num_analysers, run_time); - break; - case 16: - run_simulation<16>(num_sensors, num_analysers, run_time); - break; - case 32: - run_simulation<32>(num_sensors, num_analysers, run_time); - break; - default: - std::cout << "Nicht unterstützte Puffergröße. Verwende Standard (" - << DEFAULT_BUFFER_SIZE << ")\n"; - run_simulation( - num_sensors, num_analysers, run_time - ); - } - - std::cout << "Simulation erfolgreich abgeschlossen.\n"; + // Netzwerk mit Puffergröße 8 erstellen + SensorNetwork<8> network; + + std::cout << "Starting simulation...\n"; + + // 2 Sensoren und 2 Analyse-Module starten + network.start(2, 2); + + // 30 Sekunden laufen lassen + std::this_thread::sleep_for(std::chrono::seconds(30)); + + // Netzwerk stoppen + network.stop(); + + std::cout << "Simulation finished\n"; return 0; } diff --git a/ring_buffer.h b/ring_buffer.h index fc94a85..bb7b34c 100644 --- a/ring_buffer.h +++ b/ring_buffer.h @@ -1,63 +1,63 @@ #pragma once -#include -#include +#include #include #include +/** + * Thread-sicherer Ringpuffer mit fester Größe N + * Implementiert das Producer-Consumer-Pattern mit: + * - Mutex für exklusiven Zugriff + * - Condition Variable für blockierendes Lesen + * - Überschreibt älteste Daten bei vollem Puffer + */ template class RingBuffer { - static_assert(N > 1, "Buffer size must be greater than 1"); + std::array data; // Speicher für die Elemente + size_t read = 0; // Lese-Position + size_t write = 0; // Schreib-Position + bool full = false; // Flag für vollen Puffer -private: - std::vector data; - size_t read_ptr = 0; - size_t write_ptr = 0; - bool full = false; - - std::mutex mtx; - std::condition_variable not_empty; - - size_t advance(size_t ptr) const { - return (ptr + 1) % N; - } + std::mutex mtx; // Schützt alle Zugriffe + std::condition_variable cv; // Synchronisiert Leser public: - RingBuffer() : data(N, 0) {} - + /** + * Schreibt einen Wert in den Puffer + * @param value Der zu schreibende Wert + * + * Funktionsablauf: + * 1. Sperrt den Puffer mit Mutex + * 2. Schreibt Wert an aktueller Position + * 3. Überschreibt ältesten Wert wenn voll + * 4. Aktualisiert Schreib-Position + * 5. Benachrichtigt wartende Leser + */ void push(int value) { - std::unique_lock lock(mtx); - - data[write_ptr] = value; - - if(full) { - read_ptr = advance(read_ptr); - } - - write_ptr = advance(write_ptr); - full = (write_ptr == read_ptr); - - not_empty.notify_one(); + std::lock_guard lock(mtx); + data[write] = value; + write = (write + 1) % N; // Ringverhalten + if (full) read = (read + 1) % N; // Überschreiben + full = (write == read); // Update Voll-Flag + cv.notify_one(); // Wecke einen Leser } + /** + * Liest einen Wert aus dem Puffer (blockierend) + * @return Der gelesene Wert + * + * Funktionsablauf: + * 1. Sperrt den Puffer + * 2. Wartet bis Daten verfügbar + * 3. Liest Wert und aktualisiert Position + * 4. Gibt Wert zurück + */ int pop() { std::unique_lock lock(mtx); - - not_empty.wait(lock, [this]() { - return !is_empty(); - }); - - int value = data[read_ptr]; - read_ptr = advance(read_ptr); - full = false; - - return value; - } - - bool is_empty() const { - return !full && (read_ptr == write_ptr); - } - - bool is_full() const { - return full; + // Warte bis Daten da sind (verhindert Busy Waiting) + cv.wait(lock, [this]{ return full || write != read; }); + int val = data[read]; + read = (read + 1) % N; // Ringverhalten + full = false; // Nicht mehr voll + return val; } }; diff --git a/sensor_network b/sensor_network index e01a289..9d99c42 100755 Binary files a/sensor_network and b/sensor_network differ diff --git a/sensor_network.cpp b/sensor_network.cpp index 65b5247..bd8c6fd 100644 --- a/sensor_network.cpp +++ b/sensor_network.cpp @@ -3,92 +3,77 @@ #include #include +/** + * Startet das Sensornetzwerk + * @param sensors Anzahl der Sensor-Threads + * @param analysers Anzahl der Analyse-Threads + */ template -void SensorNetwork::start(size_t num_sensors, size_t num_analysers) { +void SensorNetwork::start(size_t sensors, size_t analysers) { running = true; - - for(size_t i = 0; i < num_sensors; ++i) { - sensors.emplace_back([this, i] { - sensor_thread(i); + + // Sensor-Threads erstellen + for (size_t i = 0; i < sensors; ++i) { + threads.emplace_back([this] { + std::mt19937 gen(std::random_device{}()); + std::uniform_int_distribution<> dist(0, 100); + + while (running) { + // Zufälliges Intervall (100-500ms) + std::this_thread::sleep_for( + std::chrono::milliseconds(100 + gen() % 400)); + + // Messwert generieren und speichern + buffer.push(dist(gen)); + } }); } - for(size_t i = 0; i < num_analysers; ++i) { - analysers.emplace_back([this, i] { - analyser_thread(i); + // Analyse-Threads erstellen + for (size_t i = 0; i < analysers; ++i) { + threads.emplace_back([this] { + while (running) { + // Daten aus Puffer lesen + int data = buffer.pop(); + + // Analysemodell lesen + int model_val = model.read(); + + // Ausgabe (könnte auch analysieren) + std::cout << "Data: " << data + << " Model: " << model_val << "\n"; + } }); } - controller = std::thread([this] { - controller_thread(); + // Controller-Thread erstellen + threads.emplace_back([this] { + std::mt19937 gen(std::random_device{}()); + while (running) { + // Zufälliges Update-Intervall (500-2000ms) + std::this_thread::sleep_for( + std::chrono::milliseconds(500 + gen() % 1500)); + + // Analysemodell aktualisieren + model.write(gen() % 100); + } }); } +/** + * Stoppt das Sensornetzwerk und wartet auf Threads + */ template void SensorNetwork::stop() { - running = false; - - for(auto& t : sensors) { + running = false; // Signal zum Stoppen + + // Auf alle Threads warten + for (auto& t : threads) { if (t.joinable()) t.join(); } - for(auto& t : analysers) { - if (t.joinable()) t.join(); - } - if (controller.joinable()) { - controller.join(); - } -} - -template -void SensorNetwork::sensor_thread(int id) { - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> data_gen(0, 100); - std::uniform_int_distribution<> sleep_gen(100, 500); - - while(running) { - std::this_thread::sleep_for( - std::chrono::milliseconds(sleep_gen(gen)) - ); - - int value = data_gen(gen); - buffer.push(value); - - std::cout << "Sensor " << id << " produced: " << value << "\n"; - } -} - -template -void SensorNetwork::analyser_thread(int id) { - while(running) { - int data = buffer.pop(); - - int model_value = model.read(); - - std::cout << "Analyser " << id << " processed: " << data - << " | Model: " << model_value << "\n"; - } -} - -template -void SensorNetwork::controller_thread() { - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> update_gen(0, 100); - std::uniform_int_distribution<> sleep_gen(500, 2000); - - while(running) { - std::this_thread::sleep_for( - std::chrono::milliseconds(sleep_gen(gen)) - ); - - int new_value = update_gen(gen); - model.write(new_value); - - std::cout << "Controller updated model to: " << new_value << "\n"; - } } +// Explizite Instanziierungen für gängige Puffergrößen template class SensorNetwork<8>; template class SensorNetwork<16>; template class SensorNetwork<32>; diff --git a/sensor_network.h b/sensor_network.h index 468af5f..af314c2 100644 --- a/sensor_network.h +++ b/sensor_network.h @@ -5,26 +5,25 @@ #include "ring_buffer.h" #include "analysis_model.h" +/** + * Hauptklasse für das Sensornetzwerk + * @tparam N Größe des Ringpuffers + * + * Verwaltet alle Komponenten: + * - Ringpuffer für Sensordaten + * - Analysemodell + * - Threads für Sensoren, Analyse und Controller + */ template class SensorNetwork { - RingBuffer buffer; - AnalysisModel model; - std::atomic running{false}; - - std::vector sensors; - std::vector analysers; - std::thread controller; + RingBuffer buffer; // Gemeinsamer Datenpuffer + AnalysisModel model; // Geteiltes Analysemodell + std::atomic running = false; // Steuerflag für Threads + std::vector threads; // Alle Threads public: - ~SensorNetwork() { - if (running) stop(); - } + ~SensorNetwork() { if (running) stop(); } - void start(size_t num_sensors, size_t num_analysers); + void start(size_t sensors, size_t analysers); void stop(); - -private: - void sensor_thread(int id); - void analyser_thread(int id); - void controller_thread(); };