diff --git a/README.md b/README.md index a5689b8..4653416 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,5 +151,4 @@ 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 4b4be10..e775eb5 100644 --- a/analysis_model.h +++ b/analysis_model.h @@ -1,75 +1,32 @@ #pragma once #include -#include /** - * Implementiert das Reader-Writer Problem mit: - * - Mehrere gleichzeitige Leser - * - Exklusiver Zugriff für Schreiber - * - Verhindert Writer-Starvation + * Thread-sicheres Analysemodell + * Vereinfachte Implementierung mit: + * - Einfachem Mutex-Schutz (kein Reader-Writer-Lock) + * - Für seltene Schreibzugriffe geeignet */ class AnalysisModel { - int value = 0; // Das geteilte Analysemodell (vereinfacht) - int reader_count = 0; // Zählt aktive Leser - - // Synchronisationsprimitive - std::mutex model_mutex; // Schützt Schreibzugriffe (exklusiv) - std::mutex count_mutex; // Schützt Leserzähler - std::condition_variable no_writer; // Garantiert Fairness + int value = 0; // Der gespeicherte Wert + std::mutex mtx; // Schützt Lese/Schreibzugriffe public: /** - * Lesender Zugriff - * @return Aktueller Wert des Modells - * - * Funktionsweise: - * 1. Sperrt count_mutex und inkrementiert reader_count - * 2. Erster Leser sperrt model_mutex (blockiert Writer) - * 3. Entsperrt count_mutex während des Lesens - * 4. Liest Wert - * 5. Sperrt count_mutex zum Dekrementieren - * 6. Letzter Leser entsperrt model_mutex und benachrichtigt Writer + * Liest den aktuellen Wert + * @return Der gespeicherte Wert */ int read() { - std::unique_lock count_lock(count_mutex); - reader_count++; - - // Erster Leser sperrt für Writer - if(reader_count == 1) { - model_mutex.lock(); - } - count_lock.unlock(); - - // Kritischer Abschnitt (Lesen, kann parallel erfolgen) - int result = value; - - count_lock.lock(); - reader_count--; - // Letzter Leser gibt für Writer frei - if(reader_count == 0) { - model_mutex.unlock(); - no_writer.notify_one(); - } - - return result; + std::lock_guard lock(mtx); + return value; } /** - * Schreibender Zugriff - * @param new_value Neuer Wert für das Modell - * - * Funktionsweise: - * 1. Sperrt model_mutex (exklusiver Zugriff) - * 2. Schreibt neuen Wert - * 3. Wartet bis alle Leser fertig sind (Starvation Prevention) + * Schreibt einen neuen Wert + * @param new_val Der neue Wert */ - void write(int new_value) { - std::unique_lock lock(model_mutex); - value = new_value; - - // Verhindert Writer-Starvation - no_writer.wait(lock, [this]() { - return reader_count == 0; - }); + void write(int new_val) { + std::lock_guard lock(mtx); + value = new_val; } -}; \ No newline at end of file +}; diff --git a/main.cpp b/main.cpp index b608ad5..0b81389 100644 --- a/main.cpp +++ b/main.cpp @@ -1,90 +1,26 @@ #include "sensor_network.h" #include -#include -#include -#include - -// Standardkonfiguration -constexpr size_t DEFAULT_NUM_SENSORS = 3; -constexpr size_t DEFAULT_NUM_ANALYSERS = 2; -constexpr int DEFAULT_RUN_TIME = 30; // Sekunden -constexpr size_t DEFAULT_BUFFER_SIZE = 8; /** - * Führt die Simulation mit gegebenen Parametern aus - * @tparam N Puffergröße + * Hauptprogramm + * Startet die Simulation mit festen Parametern + * (Könnte leicht für interaktive Eingabe erweitert werden) */ -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"; -} - -/** - * Liest Benutzereingabe mit Standardwert - * @param prompt Eingabeaufforderung - * @param default_value Standardwert bei leerer Eingabe - * @return Eingegebener oder Standardwert - */ -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); - - // Verwende Standardwert bei leerer Eingabe - if(input.empty()) return default_value; - - // Konvertiere Eingabe - try { - return std::stoul(input); - } catch(...) { - std::cout << "Ungültige Eingabe. Verwende Standardwert: " - << default_value << "\n"; - return default_value; - } -} - int main() { - std::cout << "=== Sensornetzwerk-Simulation ===\n" - << "(Leere Eingabe verwendet Standardwerte)\n"; - - // Interaktive Konfiguration - 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); - - // Starte Simulation basierend auf Puffergröße - 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; -} \ No newline at end of file +} diff --git a/ring_buffer.h b/ring_buffer.h index ee992c8..bb7b34c 100644 --- a/ring_buffer.h +++ b/ring_buffer.h @@ -1,104 +1,63 @@ #pragma once -#include -#include +#include #include #include /** - * Thread-sicherer Ringpuffer mit fester Größe - * @tparam N Größe des Puffers (muss > 1 sein) - * - * Implementiert das Producer-Consumer Pattern mit: + * Thread-sicherer Ringpuffer mit fester Größe N + * Implementiert das Producer-Consumer-Pattern mit: * - Mutex für exklusiven Zugriff - * - Condition Variable für blockierendes Pop + * - 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; // Speicher für Elemente - size_t read_ptr = 0; // Lesezeiger (nächstes zu lesendes Element) - size_t write_ptr = 0; // Schreibzeiger (nächstes freie Position) - bool full = false; // Flag, ob Puffer voll ist - - // Synchronisationsprimitive - std::mutex mtx; // Schützt alle internen Zustände - std::condition_variable not_empty; // Signalisiert, dass Daten verfügbar sind - - // Hilfsfunktion: Zeiger mit Ringverhalten bewegen - 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 Wert in den Puffer + * Schreibt einen Wert in den Puffer * @param value Der zu schreibende Wert * - * Funktionsweise: - * 1. Sperrt Mutex für exklusiven Zugriff - * 2. Schreibt Wert an aktueller write_ptr - * 3. Bei vollem Puffer: Bewegt read_ptr (überschreibt ältesten Wert) - * 4. Aktualisiert write_ptr und full-Flag - * 5. Benachrichtigt einen wartenden Consumer + * 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); - - // Schreibe Wert - data[write_ptr] = value; - - // Überschreibe ältesten Wert bei vollem Puffer - if(full) { - read_ptr = advance(read_ptr); - } - - // Zeiger aktualisieren - write_ptr = advance(write_ptr); - full = (write_ptr == read_ptr); - - // Benachrichtige einen wartenden Consumer - 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 Wert aus dem Puffer (blockierend) + * Liest einen Wert aus dem Puffer (blockierend) * @return Der gelesene Wert * - * Funktionsweise: - * 1. Sperrt Mutex - * 2. Wartet mit Condition Variable bis Daten verfügbar - * 3. Liest Wert an read_ptr - * 4. Aktualisiert read_ptr und full-Flag - * 5. Gibt Wert zurück + * 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); - - // Warte bis Daten verfügbar (verhindert Busy Waiting) - not_empty.wait(lock, [this]() { - return !is_empty(); - }); - - // Lese und aktualisiere Zustand - int value = data[read_ptr]; - read_ptr = advance(read_ptr); - full = false; - - return value; + // 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; } - - // Prüft ob Puffer leer ist - bool is_empty() const { - return !full && (read_ptr == write_ptr); - } - - // Prüft ob Puffer voll ist - bool is_full() const { - return full; - } -}; \ No newline at end of file +}; diff --git a/sensor_network b/sensor_network index a918fbc..9d99c42 100755 Binary files a/sensor_network and b/sensor_network differ diff --git a/sensor_network.cpp b/sensor_network.cpp index 038a7ca..bd8c6fd 100644 --- a/sensor_network.cpp +++ b/sensor_network.cpp @@ -4,135 +4,76 @@ #include /** - * Startet alle Threads des Netzwerks - * @param num_sensors Anzahl der Sensor-Threads - * @param num_analysers Anzahl der Analyse-Threads + * 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; - - // Starte Sensor-Threads - 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)); + } }); } - // Starte Analyse-Threads - 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"; + } }); } - // Starte Controller-Thread - 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 alle Threads und wartet auf Beendigung + * Stoppt das Sensornetzwerk und wartet auf Threads */ template void SensorNetwork::stop() { - running = false; - - // Warte auf Thread-Ende - 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(); - } } -/** - * Thread-Funktion für Sensoren (Producer) - * @param id Eindeutige ID des Sensors - * - * Funktionsweise: - * 1. Generiert zufällige Messwerte - * 2. Wartet zufällige Zeit (Messintervall) - * 3. Schreibt Daten in Ringpuffer - */ -template -void SensorNetwork::sensor_thread(int id) { - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> data_gen(0, 100); // Messwerte 0-100 - std::uniform_int_distribution<> sleep_gen(100, 500); // Intervall 100-500ms - - while(running) { - // Simuliere Messintervall - std::this_thread::sleep_for( - std::chrono::milliseconds(sleep_gen(gen)) - ); - - // Generiere und schreibe Messwert - int value = data_gen(gen); - buffer.push(value); - - std::cout << "Sensor " << id << " produced: " << value << "\n"; - } -} - -/** - * Thread-Funktion für Analyse-Module (Consumer) - * @param id Eindeutige ID des Moduls - * - * Funktionsweise: - * 1. Liest Daten aus Ringpuffer (blockierend) - * 2. Liest aktuelles Analysemodell - * 3. Verarbeitet Daten (hier nur Ausgabe) - */ -template -void SensorNetwork::analyser_thread(int id) { - while(running) { - // Blockierendes Lesen aus Puffer - int data = buffer.pop(); - - // Lesender Zugriff auf Analysemodell - int model_value = model.read(); - - std::cout << "Analyser " << id << " processed: " << data - << " | Model: " << model_value << "\n"; - } -} - -/** - * Thread-Funktion für System-Controller (Writer) - * - * Funktionsweise: - * 1. Wartet zufällige Zeit zwischen Updates - * 2. Schreibt neuen Wert ins Analysemodell - */ -template -void SensorNetwork::controller_thread() { - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> update_gen(0, 100); // Modellwerte - std::uniform_int_distribution<> sleep_gen(500, 2000); // Update-Intervall - - while(running) { - // Warte bis zum nächsten Update - std::this_thread::sleep_for( - std::chrono::milliseconds(sleep_gen(gen)) - ); - - // Aktualisiere Analysemodell - int new_value = update_gen(gen); - model.write(new_value); - - std::cout << "Controller updated model to: " << new_value << "\n"; - } -} - -// Explizite Instanziierung für gängige Puffergrößen +// Explizite Instanziierungen für gängige Puffergrößen template class SensorNetwork<8>; template class SensorNetwork<16>; -template class SensorNetwork<32>; \ No newline at end of file +template class SensorNetwork<32>; diff --git a/sensor_network.h b/sensor_network.h index 1480dc3..af314c2 100644 --- a/sensor_network.h +++ b/sensor_network.h @@ -6,7 +6,7 @@ #include "analysis_model.h" /** - * Hauptklasse des Sensornetzwerks + * Hauptklasse für das Sensornetzwerk * @tparam N Größe des Ringpuffers * * Verwaltet alle Komponenten: @@ -16,26 +16,14 @@ */ template class SensorNetwork { - RingBuffer buffer; // Gemeinsamer Datenpuffer - AnalysisModel model; // Geteiltes Analysemodell - std::atomic running{false}; // Steuerflag für Threads - - // Thread-Container - 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: - // Thread-Funktionen - void sensor_thread(int id); - void analyser_thread(int id); - void controller_thread(); -}; \ No newline at end of file +};