【问题标题】:Properly update class members正确更新班级成员
【发布时间】:2018-03-21 16:57:33
【问题描述】:

我正在尝试编写一个解决 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 发生了什么的信息。我想把它做成这样:我有一个全球哲学家的载体。每隔 200 毫秒,我的另一个线程使用 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() 函数)中的线程改变了一些东西,这个改变不会出现在全局向量中。有没有办法改变这种行为?

【问题讨论】:

标签: c++ multithreading ncurses


【解决方案1】:

创建线程的过程是你的问题。

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]));

push_back 方法中的临时对象philosopher 被移动,因此被移动的对象获得了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),

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多