I am trying to write a program solving 5 dining philosophers problem. I am on the beginning, so for now I want every philosopher to just think, eat, without any synchronization, looking at forks etc. This is what I wrote:
#pragma once
#include <atomic>
#include <chrono>
#include <mutex>
#include <random>
#include <thread>
#include <vector>
#include <fork.hpp>
class dining_philosophers {
public:
// std::vector<philosopher> philosophers;
std::array<fork, 5> forks;
// ui u;
dining_philosophers();
std::atomic<bool> ready{false};
};
dining_philosophers::dining_philosophers() {}
class philosopher {
public:
dining_philosophers &table;
// ui &u;
fork &left_fork;
fork &right_fork;
std::mt19937 rng{std::random_device{}()};
int state = -1;
int progress = 0;
int id;
std::thread t;
philosopher();
philosopher(int _id, dining_philosophers &table_ref, fork l, fork r)
: id(_id), left_fork(l), right_fork(r), table(table_ref),
t(&philosopher::live, this) {}
void live();
void eat();
void think();
void wait_for_forks();
void release_forks();
};
void philosopher::live() {
while (!table.ready) {
std::this_thread::yield();
}
while (true) {
think();
// wait_for_forks();
eat();
// release_forks();
}
}
void philosopher::think() {
state = 0;
int part = std::uniform_int_distribution<int>(15, 25)(rng);
int thinkingTime = part * 200; // in miliseconds
for (auto i = 0; i < part; i++) {
double p = (double)i / (double)part;
progress = p * 100;
// std::thread t(&ui::update_state, &u, id, "thinking", progress);
// u.update_state(id, "thinking", progress);
// t.join();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
void philosopher::eat() {
state = 1;
int part = std::uniform_int_distribution<int>(15, 25)(rng);
int thinkingTime = part * 200; // in miliseconds
for (auto i = 0; i < part; i++) {
double p = (double)i / (double)part;
progress = p * 100;
// std::thread t(&ui::update_state, &u, id, "thinking", progress);
// u.update_state(id, "thinking", progress);
// t.join();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
class fork
is just empty, I wrote it to use it later.
Now I need to print some information about what's going on using ncurses. I thought about making it like this: I have a global vector of philosophers. Every 200ms my other thread using ui::update()
function checks for philosophers members, like id, state and progress and prints it out. I wrote something like this:
#include <iostream>
#include <ncurses.h>
#include <thread>
#include <vector>
#include <dining_philosophers.hpp>
std::vector<philosopher> philosophers;
class ui {
private:
int row;
int col;
std::mutex m;
public:
ui();
~ui();
void print_initial_state();
void update_state(int id, const char *state, int progress);
void update();
};
ui::ui() {
initscr();
// noecho();
start_color();
getmaxyx(stdscr, col, row);
}
ui::~ui() { endwin(); }
void ui::update() {
int x = 10;
int y = 10;
while (true) {
for (auto &phil : philosophers) {
int id = phil.id;
int state = phil.state;
int progress = phil.progress;
move(y + id - 1, 0);
clrtoeol();
move(y + id - 1, x);
printw("Philosopher %d is %d, progress: %d%%", id, state, progress);
refresh();
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
int main() {
dining_philosophers table;
ui u;
// std::vector<philosopher> philosophers;
for (auto i = 0; i < 4; i++) {
philosophers.push_back(
philosopher(i + 1, table, table.forks[i], table.forks[i + 1]));
}
philosophers.push_back(
philosopher(5, table, table.forks[4], table.forks[0]));
// std::thread t{[&]() {}};
std::this_thread::sleep_for(std::chrono::seconds(1));
table.ready = true;
std::thread t1(&ui::update, &u);
// std::this_thread::sleep_for(std::chrono::seconds(5));
// t.join();
t1.join();
for (auto &p : philosophers) {
p.t.join();
}
}
I know I don't have a proper thread closing, for now I use Ctrl+C. The problem is that ncurses for five philosophers prints:
Philosopher 1 is -1, progress: 0%
Philosopher 2 is -1, progress: 0%
and so on. If in update function I would do philosophers[0].progress++
, it would start to increment the progress. So I guess the problem is that if the thread in philosopher (live()
function) changes something, the change does not appear in the global vector. Is there a way to change that behaviour?