使用C ++中的线程来报告计算进度 [英] Using a thread in C++ to report progress of computations

查看:167
本文介绍了使用C ++中的线程来报告计算进度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

能够看到 a b 的值,而无需修改循环。在过去,我写了如下语句如下:

  int a,b; 
for(int i = 0; i <10000; ++ i){
for(int j = 0; j <1000; ++ j){
for = 0; k <1000; ++ k){
a = i;
b = j;
if(a%100 == 0){
printf(a =%d \\\
,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,每隔%d秒报告一次... \\\
,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);
//错误的剩余时间的添加,需要固定
//但不重要的时刻。
// totalTime + = sleepT - remainingSleepTime.tv_sec;
long sec = remainingSleepTime.tv_sec;
if(!stdstream)fp = fopen(filename,a)
fprintf(fp,report for%g seconds.\\\
,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-> gtp);
rep-> sleep();
}
}

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

  #includereporter.h
int main(){
//我们要跟踪的对象的声明
int a = 0;
int b = 0;
//记者申报
class prog_reporter:public reporter {
public:
int&一个;
int& b;
prog_reporter(int& a_,int& b_):
a(a_),b(b_),
reporter(3,stdout)
{}
void report (){
fprintf(fp,(a,b)=(%d,%d)\\\
,this-> a,this-> b)
}
};
//每3秒开始跟踪a和b
prog_reporter rep(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;
}
}
}
//停止报告
rep.end_report();
}



在编译此代码(无优化标志)并运行之后,我获得: / p>

  macbook-pro:Desktop jmlopez $ g ++ testing.cpp 
macbook-pro:Desktop jmlopez $ ./a.out
每3秒报告一次...
(a,b)=(0,60)
(a,b)=(1497,713)
=(2996,309)
(a,b)=(4497,478)
(a,b)=(5996,703)

(a,b)=(8915,78)
报告18秒。

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

  macbook-pro:Desktop jmlopez $ g ++ testing.cpp -O3 
macbook-pro:Desktop jmlopez $ ./a.out
每3秒报告一次...
(a,b)=(0,0)
报告0秒。

这并不奇怪,因为编译器可能重写我的代码给我相同的答案在一个更短的多少时间。我原来的问题是为什么记者没有给我的变量的值,如果我让循环更长,例如:

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

优化标志:

  macbook-pro:桌面jmlopez $ g ++ testing.cpp -O3 
macbook-pro:桌面jmlopez $ ./a.out
每3秒报告一次...
(a,b)=(0,0)
(a,b)= $ b(a,b)=(0,0)
(a,b)=(0,0)
b)=(0,0)
(a,b)=(0,0)
(a,b) ,0)
(a,b)=(0,0)
(a,b)=(0,0)
$ b(a,b)=(0,0)
(a,b)=(0,0)
报告39秒。

问题:这是由于优化标志修改代码



主要问题:



方法 end_report 我调用函数 pthread_cancel 。阅读以下回答后,它使我对该函数的使用和我如何终止报告线程产生疑问。对于那些使用 pthreads 的人来说,使用线程是否有明显的漏洞或潜在问题? p>

解决方案

关于主要问题:你很近。添加对 pthread_join() http: //linux.die.net/man/3/pthread_join )之后, pthread_cancel(),一切都应该很好。



连接调用确保你清理线程资源,如果被遗忘,在某些情况下可能会导致线程资源耗尽。



<只需添加,使用 pthread_cancel()(除了记住加入线程)的重要一点是确保你取消的线程有一个so - 呼叫取消点,您的主题通过调用 nanosleep()(也可能还包括 fopen fprintf fclose 其中可以是取消点)。如果没有取消点,你的线程将继续运行。


I'm writing a generic abstract class to be able to report on the status of as many instance variables as we need. For instance, consider the following useless loop:

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;
        }
    }
}

It would be nice to be able to see the values of a and b without having to modify the loop. In the past I have written if statements such as the following:

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

This would allow me to see the value of a every 100 iterations. However, depending on the computations being done, sometimes it is just not possible to check on the progress in this fashion. The idea is to have be able to go away from the computer, come back after a given time and check on whatever values you want to see.

To this end we can use pthreads. The following code works, and the only reason I am posting it is because I'm not sure if I'm using the thread correctly, mainly, how to shut it off.

First lets consider the file "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();
    }
}

This file declares the abstract class reporter, notice the pure virtual function report. This is the function that will print the messages. Each reporter has its own thread and the thread gets created when the reporter constructor is called. To use the reporter object in our useless loop now we can do:

#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();
}

After compiling this code (no optimization flag) and running it I obtain:

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.

This does exactly what I wanted it to do, with the optimization flags then I get:

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.

Which is not surprising since the compiler probably rewrote my the code to give me the same answer in a shorter amount of time. My original question was going to be why the reporter did not give me the values of the variables if I made the loops longer, for instance:

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;
        }
    }
}

After running the code again with the optimization flag:

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.

Question: Is this output due to the optimization flag which modifies the code and it simply decides not to update the variables til the very end?

Main question:

In the reporter method end_report I call the function pthread_cancel. After reading the following answer it made me doubtful about the use of the function and how I was terminating the reporting thread. For those experienced with pthreads, is there any obvious holes or potential problems using the thread as I have done?

解决方案

About the main question: You're close. Add a call to pthread_join() (http://linux.die.net/man/3/pthread_join) after pthread_cancel(), and everything should be fine.

The join call makes sure that you clean up the threads resources, and, if forgotten, can lead to running out of threading resources in certain cases.

And just to add, the important point when using pthread_cancel() (apart from remembering to join the thread) is to make sure that the thread you are canceling has a so-called cancellation point, which your thread does by calling nanosleep() (and possibly also fopen, fprintf and fclose which may be cancellation points). If no cancellation point exists, your thread will just keep running.

这篇关于使用C ++中的线程来报告计算进度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆