我正在编写一个通用的抽象类,以便能够根据需要报告任意数量的实例变量的状态.例如,考虑以下无用循环:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
能够看到a和b不必修改循环的值是很好的.在过去,我写过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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这将允许我看到a每100次迭代的值.但是,根据正在进行的计算,有时无法以这种方式检查进度.我们的想法是能够离开计算机,在给定时间后回来并检查您想要看到的任何值.
为此我们可以使用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();
}
}
Run Code Online (Sandbox Code Playgroud)
该文件声明了抽象类reporter,注意纯虚函数report.这是将打印消息的功能.每个记者都有自己的,thread并在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();
}
Run Code Online (Sandbox Code Playgroud)
在编译此代码(没有优化标志)并运行它后,我获得:
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.
Run Code Online (Sandbox Code Playgroud)
这正是我想要它做的事情,然后我得到优化标志:
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.
Run Code Online (Sandbox Code Playgroud)
这并不奇怪,因为编译器可能会重写我的代码,以便在更短的时间内给出相同的答案.我原来的问题是,如果我让循环更长,记者没有给我变量的值,例如:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
使用优化标志再次运行代码后:
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.
Run Code Online (Sandbox Code Playgroud)
问题:这个输出是由于优化标志修改了代码,它只是决定不更新变量直到最后?
在reporter方法中end_report我调用函数pthread_cancel.在阅读了以下答案后,它让我怀疑该功能的使用以及我如何终止报告线程.对于那些经验丰富的人来说pthreads,有没有明显的漏洞或使用thread我所做的潜在问题?
关于主要问题:你已经很接近了。之后添加对pthread_join()(http://linux.die.net/man/3/pthread_join )的调用pthread_cancel(),一切都应该没问题。
join 调用可确保您清理线程资源,如果忘记,在某些情况下可能会导致线程资源耗尽。
补充一下,使用时的重要一点pthread_cancel()(除了记住加入线程之外)是确保您要取消的线程有一个所谓的取消点,您的线程通过调用nanosleep()(也可能是fopen,fprintf并且fclose哪个可能是取消点)。如果不存在取消点,您的线程将继续运行。