diff options
author | lonkaars <loek@pipeframe.xyz> | 2023-05-13 12:45:37 +0200 |
---|---|---|
committer | lonkaars <loek@pipeframe.xyz> | 2023-05-13 12:45:37 +0200 |
commit | e95bd56df67f3ba6d6f9d44c66b791e45bcd54a3 (patch) | |
tree | 457276154858c7fe71a111842b1d377bbcc25bcc | |
parent | 17dd9cdc6d5beb636695e5becbc93275524665af (diff) |
week 3 worky
-rw-r--r-- | os2w3/.gitignore | 2 | ||||
-rw-r--r-- | os2w3/main.cpp | 130 | ||||
-rw-r--r-- | os2w3/makefile | 17 |
3 files changed, 149 insertions, 0 deletions
diff --git a/os2w3/.gitignore b/os2w3/.gitignore new file mode 100644 index 0000000..f0c9b81 --- /dev/null +++ b/os2w3/.gitignore @@ -0,0 +1,2 @@ +*.o +main diff --git a/os2w3/main.cpp b/os2w3/main.cpp new file mode 100644 index 0000000..5574fbc --- /dev/null +++ b/os2w3/main.cpp @@ -0,0 +1,130 @@ +// beetje rommelig om <stdlib.h> te gebruiken inplaats van <cstdlib> in c++ :( +// verder worden ze ook helemaal niet gebruikt dus ik weet niet waarom het hier staat +// #include <cstdlib> +// #include <cstdio> +// #include <ctime> +#include <iostream> +#include <random> +#include <thread> +#include <mutex> +#include <condition_variable> +using namespace std; + +const int TICK = 1000; +const int NBOXES = 10; +const int BUFLEN = 4; + +// https://stackoverflow.com/questions/21237905/how-do-i-generate-thread-safe-uniform-random-numbers +int random(int n) { + static thread_local std::mt19937 generator; + std::uniform_int_distribution<int> distribution(0, n); + return distribution(generator); +} + +class Box { +private: + const char *name; + int number; +public: + Box(const char *name, int number) { + this->name = name; + this->number = number; + } + friend ostream &operator << (ostream &out, Box *b) { + return out << b->name << "-" << b->number; + } +}; + +class Queue { +private: + Box *buffer[BUFLEN] = { nullptr }; + + int getpos = 0, putpos = 0; + int count = 0; + + mutex _busy; + // deze mutex zorgt ervoor dat alle variabelen binnen de critical section + // (`buffer`, `getpos`, `putpos`, `count`, `_can_get`, en `_can_put`) + // allemaal door maar één thread tegelijkertijd aangepast kunnen worden, + // waardoor deze klasse een thread-safe queue implementeert + condition_variable _can_get; + condition_variable _can_put; + +public: + Queue() { } + // initialisatiewaarden van private variabelen bij de declaratie neergezet, + // de std library mutex en unique_lock hebben geen expliciete initialisatie + // nodig + + Box *get(const char *consumername) { + unique_lock<mutex> lock(_busy); + // deze mutex komt overeen met de windows api code die aan het begin van + // een functie EnterCriticalSection doet, en aan het einde + // LeaveCriticalSection (maar dit gebeurt impliciet omdat de variabele + // `lock` buiten de scope valt bij de return van deze functie) + while (count == 0) // wacht tot er minstens een doos is om te pakken + _can_get.wait(lock); // wacht tot een melding op _can_get (vermijd CPU kachel) + // de bovenstaande while loop met wait gedoe is gelijk aan WaitForSingleObject + Box *box = buffer[getpos]; + getpos = (getpos + 1) % BUFLEN; + count--; + cout << consumername << ": gets " << box << endl; + _can_put.notify_one(); // meld aan threads die wachten op _can_put dat + // count anders is geworden + return box; + } + + // dezelfde structuur wordt gebruikt voor thread safety als de bovenstaande + // functie, maar dan met _can_put en _can_get omgedraaid + void put(const char *producername, Box *box) { + unique_lock<mutex> lock(_busy); + while (count == BUFLEN) _can_put.wait(lock); + cout << producername << ": puts " << box << endl; + buffer[putpos] = box; + putpos = (putpos + 1) % BUFLEN; + count++; + _can_get.notify_one(); + } +}; + +Queue q; + +// thread functie hoeft verder niks te returnen (waarom DWORD WINAPI ??) +void produce(void *arg) { + char *name = (char*) arg; + for(int i = 0; i < NBOXES; i++) { + // c++ stdlib sleep :tada: + std::this_thread::sleep_for(std::chrono::milliseconds(random(TICK))); + Box *box = new Box(name, i); + q.put(name, box); + } +} + +void consume(void *arg) { + char *name = (char*) arg; + for(int i = 0; i < NBOXES; i++) { + std::this_thread::sleep_for(std::chrono::milliseconds(random(TICK))); + Box *box = q.get(name); + delete box; + } +} + +int main(int argc, char* argv[]) { + // c++ stdlib threads :tada: + vector<thread> pool; + + pool.push_back(thread(produce, (void*)"P1")); + pool.push_back(thread(produce, (void*)"P2")); + pool.push_back(thread(produce, (void*)"P3")); + pool.push_back(thread(produce, (void*)"P4")); + + pool.push_back(thread(consume, (void*)"C1")); + pool.push_back(thread(consume, (void*)"C2")); + pool.push_back(thread(consume, (void*)"C3")); + pool.push_back(thread(consume, (void*)"C4")); + + for (thread& t : pool) t.join(); + + return 0; +} + diff --git a/os2w3/makefile b/os2w3/makefile new file mode 100644 index 0000000..258d688 --- /dev/null +++ b/os2w3/makefile @@ -0,0 +1,17 @@ +CPP = g++ +LD = g++ + +LFLAGS += -lstdc++ +CFLAGS += -g + +all: main + +main: main.o + $(LD) $^ $(LFLAGS) -o $@ + +%.o: %.cpp + $(CPP) -c $(CFLAGS) $< -o $@ + +clean: + $(RM) main.o main + |