正确更新班级成员

时间:2018-03-21 16:57:34

标签: c++ multithreading ncurses

我正在努力编写一个解决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()函数)中的线程发生了变化,那么变化就不会出现在全局向量中。有没有办法改变这种行为?

1 个答案:

答案 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_forkright_fork是引用,现在绑定对临时对象的引用 - lr,将其更改为

philosopher(int _id, dining_philosophers &table_ref, fork& l, fork& r)
    : id(_id), left_fork(l), right_fork(r), table(table_ref),