// beetje rommelig om te gebruiken inplaats van in c++ :( // verder worden ze ook helemaal niet gebruikt dus ik weet niet waarom het hier staat // #include // #include // #include #include #include #include #include #include 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 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 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 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 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; }