我正在努力编写一个解决5个餐饮哲学家问题的程序。我刚开始,所以现在我希望每个哲学家只是思考,吃饭,没有任何同步,看着叉子等等。这就是我写的:
#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
只是空的,我写了以后再使用它。
现在我需要打印一些关于使用ncurses进行操作的信息。我想这样做:我有一个哲学家的全球载体。每隔200ms,我的另一个线程使用ui::update()
函数检查哲学家成员,如id,状态和进度并将其打印出来。我写了这样的话:
#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();
}
}
我知道我没有正确的线程关闭,现在我使用 Ctrl + C 。问题是五位哲学家的ncurses印刷品:
Philosopher 1 is -1, progress: 0%
Philosopher 2 is -1, progress: 0%
等等。如果在更新函数中我会philosophers[0].progress++
,它将开始增加进度。所以我想问题是如果哲学家(live()
函数)中的线程发生了变化,那么变化就不会出现在全局向量中。有没有办法改变这种行为?
答案 0 :(得分:0)
创建线程的过程是你的问题。
for (auto i = 0; i < 4; i++) {
philosophers.push_back(philosopher(i + 1, table, table.forks[i], table.forks[i + 1]));
}
在上面的代码中,您创建了插入到向量中的philosopher
对象。在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) {} // <<<<<---------------------
创建t
线程并将其作为线程函数 - live
方法,并在this
对象上调用此方法。
致电后
philosophers.push_back(philosopher(i + 1, table, table.forks[i], table.forks[i + 1]));
移动philosopher
方法中的临时对象push_back
,因此移动的对象获取this
指针的新值。在调用push_back
方法重新分配内存时,也会移动所有创建的对象。这种构造对象的方法是问题 - 线程函数指的是不存在的对象,对象被移动,因此其this
指针无效。
对我来说,你应该创建所有对象然后你可以启动线程。
1)删除
t(&philosopher::live, this) {}
来自构造函数
2)在philosopher
类中添加新方法以启动线程
void startTask () {
t = thread(&philosopher::live,this);
}
3)创建并启动线程
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]));
for (int i = 0; i < 5; ++i)
philosophers[i].startTask();
最后一个问题,你当前的构造函数看起来像
philosopher(int _id, dining_philosophers &table_ref, fork l, fork r)
: id(_id), left_fork(l), right_fork(r), table(table_ref),
left_fork
和right_fork
是引用,现在绑定对临时对象的引用 - l
和r
,将其更改为
philosopher(int _id, dining_philosophers &table_ref, fork& l, fork& r)
: id(_id), left_fork(l), right_fork(r), table(table_ref),