c++ - 在 C++ 中使用线程报告计算进度

标签 c++ unix pthreads

我正在编写一个通用抽象类,以便能够根据需要报告尽可能多的实例变量的状态。例如,考虑以下无用循环:

int a, b;
for (int i=0; i < 10000; ++i) {
    for (int j=0; j < 1000; ++j) {
        for (int k =0; k < 1000; ++k) {
            a = i;
            b = j;
        }
    }
}

如果能够在不修改循环的情况下查看 ab 的值,那就太好了。过去我写过如下 if 语句:

int a, b;
for (int i=0; i < 10000; ++i) {
    for (int j=0; j < 1000; ++j) {
        for (int k =0; k < 1000; ++k) {
            a = i;
            b = j;
            if (a % 100 == 0) {
                printf("a = %d\n", a);
            }
        }
    }
}

这将允许我每 100 次迭代查看 a 的值。然而,根据正在进行的计算,有时无法以这种方式检查进度。我们的想法是能够离开计算机,在给定时间后返回并检查您想要查看的任何值。

为此我们可以使用pthreads。以下代码有效,我发布它的唯一原因是因为我不确定我是否正确使用线程,主要是如何关闭它。

首先让我们考虑文件“reporter.h”:

#include <cstdio>
#include <cstdlib>
#include <pthread.h>

void* run_reporter(void*);

class reporter {
public: 
    pthread_t thread;
    bool stdstream;
    FILE* fp;

    struct timespec sleepTime;
    struct timespec remainingSleepTime;

    const char* filename;
    const int sleepT;
    double totalTime;

    reporter(int st, FILE* fp_): fp(fp_), filename(NULL), stdstream(true), sleepT(st) {
        begin_report();
    }
    reporter(int st, const char* fn): fp(NULL), filename(fn), stdstream(false), sleepT(st) {
        begin_report();
    }
    void begin_report() {
        totalTime = 0;
        if (!stdstream) fp = fopen(filename, "w");
        fprintf(fp, "reporting every %d seconds ...\n", sleepT);
        if (!stdstream) fclose(fp);
        pthread_create(&thread, NULL, run_reporter, this);
    }
    void sleep() {
        sleepTime.tv_sec=sleepT;
        sleepTime.tv_nsec=0;
        nanosleep(&sleepTime, &remainingSleepTime);
        totalTime += sleepT;
    }
    virtual void report() = 0;
    void end_report() {
        pthread_cancel(thread);
        // Wrong addition of remaining time, needs to be fixed
        // but non-important at the moment.
        //totalTime += sleepT - remainingSleepTime.tv_sec;
        long sec = remainingSleepTime.tv_sec;
        if (!stdstream) fp = fopen(filename, "a");
        fprintf(fp, "reported for %g seconds.\n", totalTime);
        if (!stdstream) fclose(fp);
    }
};

void* run_reporter(void* rep_){
    reporter* rep = (reporter*)rep_;
    while(1) {
        if (!rep->stdstream) rep->fp = fopen(rep->filename, "a");
        rep->report();
        if (!rep->stdstream) fclose(rep->fp);
        rep->sleep();
    }
}

这个文件声明了抽象类reporter,注意纯虚函数report。这是将打印消息的函数。每个报告者都有自己的线程,并且在调用reporter 构造函数时创建线程。要在我们无用的循环中使用 reporter 对象,我们现在可以这样做:

#include "reporter.h"
int main() {
    // Declaration of objects we want to track
    int a = 0;
    int b = 0;
    // Declaration of reporter
    class prog_reporter: public reporter {
    public:
        int& a;
        int& b;
        prog_reporter(int& a_, int& b_):
            a(a_), b(b_),
            reporter(3, stdout)
        {}
        void report() {
            fprintf(fp, "(a, b) = (%d, %d)\n", this->a, this->b);
        }
    };
    // Start tracking a and b every 3 seconds
    prog_reporter rep(a, b);

    // Do some useless computation
    for (int i=0; i < 10000; ++i) {
        for (int j=0; j < 1000; ++j) {
            for (int k =0; k < 1000; ++k) {
                a = i;
                b = j;
            }
        }
    }
    // Stop reporting
    rep.end_report();
}

编译此代码(无优化标志)并运行后,我得到:

macbook-pro:Desktop jmlopez$ g++ testing.cpp
macbook-pro:Desktop jmlopez$ ./a.out 
reporting every 3 seconds ...
(a, b) = (0, 60)
(a, b) = (1497, 713)
(a, b) = (2996, 309)
(a, b) = (4497, 478)
(a, b) = (5996, 703)
(a, b) = (7420, 978)
(a, b) = (8915, 78)
reported for 18 seconds.

这正是我想要的,使用优化标志然后我得到:

macbook-pro:Desktop jmlopez$ g++ testing.cpp -O3
macbook-pro:Desktop jmlopez$ ./a.out 
reporting every 3 seconds ...
(a, b) = (0, 0)
reported for 0 seconds.

这并不奇怪,因为编译器可能重写了我的代码,以便在更短的时间内给我相同的答案。我最初的问题是,如果我使循环变长,为什么记者不给我变量的值,例如:

for (int i=0; i < 1000000; ++i) {
    for (int j=0; j < 100000; ++j) {
        for (int k =0; k < 100000; ++k) {
            a = i;
            b = j;
        }
    }
}

使用优化标志再次运行代码后:

macbook-pro:Desktop jmlopez$ g++ testing.cpp -O3
macbook-pro:Desktop jmlopez$ ./a.out 
reporting every 3 seconds ...
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
(a, b) = (0, 0)
reported for 39 seconds.

问题:这个输出是由于优化标志修改了代码,它只是决定直到最后都不更新变量吗?

主要问题:

reporter 方法 end_report 中,我调用函数 pthread_cancel。看完以下answer这让我怀疑该功能的使用以及我如何终止报告线程。对于那些使用过 pthreads 的人来说,像我一样使用 thread 是否有任何明显的漏洞或潜在问题?

最佳答案

C++ 不了解线程,您的代码使用两个局部变量 ab 并且不调用具有未知代码的函数。

发生的事情是 ab 最终在寄存器中在循环期间进行计算,并且它们仅在结束时更新循环的。

虽然 ab 确实必须获得一个真实的内存地址(因为它们已作为对外部函数的引用传递),但编译器并不知道某些知道 ab 地址的外部代码将在循环期间 执行,因此倾向于将所有中间值存储到寄存器直到循环结束。

如果您在循环中的代码调用了一个未知函数(即实现未知的函数),那么编译器将被迫更新 ab 在调用该函数之前,因为它一定是偏执狂,并考虑到传递了 ab 地址的进度函数可能会将此信息传递给未知函数。

关于c++ - 在 C++ 中使用线程报告计算进度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20586968/

相关文章:

bash - Grep 命令被特殊字符反转?

unix - GNU screen : can you automatically name a window after the last invoked program?

c++ - 用户界面不在 qt 应用程序中更新

C++11 for 模板函数中的循环

c++ - 报错,即 "unresolved external symbol"

multithreading - 线程函数如何访问父线程的变量

我可以使用单个 "pthread_mutexattr_t"属性来初始化两个不同的互斥量吗?

c++ - 将字符串文字转换为 char 数组实际上在 C++ 中如何工作?

unix - 无法在 korn 脚本中的每行末尾插入日期和主机名

c++ - 什么时候使用互斥体?