C语言中报告计算进度的方法是什么?

3
这是关于在C++中使用线程报告计算进度的后续问题。
假设我有一个for循环,执行多次run_difficult_task(),我想推断出循环已经进行到哪里。我以前会这样写:
int i;
for (i=0; i < 10000; ++i) {

    run_difficult_task(i);

    if (i % 100 == 0) {
    printf("i = %d\n", i);
    }
}

但是这种方法的主要问题在于执行run_difficult_task()可能会真正地永远运行下去(被卡在无限循环中等等),因此我希望每隔k秒获得进度报告,通过打印出循环变量i的值来实现。
我在这个网站上发现了相当丰富的关于面向对象多线程的文献(我并不真正熟悉),适用于各种编程语言,但我发现在C风格中做到这一点的问题似乎已经过时了。有没有平台无关的C11方式来实现我想要的?如果没有,那么我会对在unix和gcc中工作的方法感兴趣。
注意:我不希望并行运行各种实例的run_difficult_task(例如OpenMP),而是希望同时运行for循环和报告机制。

相关文章: C语言如何进行"多线程"编程如何在纯C语言中启动线程?


把它报告给另一个进程比报告给不同的线程更容易,不是吗? - user3528438
3个回答

5

Linux(以及POSIX系统)提供了alarm库调用。这允许您在不中断主线程的情况下,在几秒钟后执行某些操作,而且当您不真正需要多线程时也不会烦扰到您。它非常适用于像您这样的使用场景。


我一定会去检查这个的。 - Matsmath
好的建议。然而,OP应该注意,以这种方式解决问题需要确保run_difficult_task()能够抵御信号中断。这是否是一个大问题取决于该函数的细节。 - John Bollinger
另一种选择几乎肯定是使run_difficult_task() 以某种方式外部通信(例如消息传递)来传递其进度。run_difficult_task() 也可以被修改为在内存支持的文件中进行低开销的存储,作为环形缓冲区,这将允许外部仪器在运行时监视进度。最后,可以使用 gdbperf 等工具随时间对进程状态进行采样。 - dho

1
你可以尝试使用一个线程(工作线程),或者可能是两个线程(一个进行计算,一个在主线程做其他事情或等待时显示输出),以及一些全局变量(呃)。
第一个线程将是您的工作马,进行计算并更新某些全局变量。第二个线程(可能只是主线程)将检查该变量是否已更改,然后打印统计信息(例如,该变量将保存统计信息,例如百分比)。
你可以尝试以下方法:
int ping = 0, working = 0, data;

// in main thread 
for (/* something */){
    // spawn worker thread
    while (working) {
        if (ping) printf("%d\n", data), ping = 0;
    }
}

// in worker thread
working = 1;
while (/* something */) {
    // do a lot of computations 
    if (/* some condition */) {
        if (! ping) {
            data = /* data */
            ping = 1;
        }
    }
}
working = 0;

你不一定需要全局变量来实现基于线程的解决方案,但你需要共享变量。如果你要实现涉及共享变量的解决方案,那么你也需要适当的同步。在这种情况下,这可能意味着通过互斥锁保护对共享变量的访问。 - John Bollinger
如果我理解正确的话,如果只有一个线程,那么您实际上建议将报告机制(例如printf)放在run_difficult_task()内部?这并没有太大帮助。 - Matsmath
@Matsmath,不对,你理解错了。"工作线程"就是你的run_difficult_taskprintf位于“//在主线程中”的位置。 - ForceBru
好的,那么为了澄清,当你说“你可以尝试使用一个线程”时,实际上是指“除了主线程外,你可以尝试使用一个额外的(=工作)线程”。因此至少需要两个线程。 - Matsmath
@Matsmath,是的。我稍微改了一下这部分的措辞,所以你可能需要查看一下编辑内容。 - ForceBru

0

这是一个我经常使用的简单的基于时间的进度指示器:

void
progress(int i)
{
    time_t tvnow;
    static time_t tvlast;
    static time_t tvbeg;

    if (tvbeg == 0) {
        tvbeg = time(NULL);
        tvlast = tvbeg - 2;
    }

    tvnow = time(NULL);
    if ((tvnow - tvlast) >= 1) {
        printf("\r%ld: i = %d",tvnow - tvbeg,i);
        fflush(stdoout);
        tvlast = tvnow;
    }
}

int i;
for (i=0; i < 10000; ++i) {
    run_difficult_task(i);
    progress(i);
}

更新:

run_difficult_task(i) 运行时间是否超过 2 秒?

不会,但我已经将示例更新为将进度代码放在单独的函数中,这是我自己代码中通常做的事情。

您需要在 run_difficult_task 中添加对进度函数的调用,以获得更好的进度控制。这也是我自己在代码中做的事情。

但请注意,我添加了一个经过时间(秒)来表示的流逝时间。

如果您不关心流逝时间,那么如果 run_difficult_task 运行时间超过 2 秒,则在其返回之前,将没有任何进度。因为进度由外部循环中的 i 加 1 来定义。

对于我的特定项目,进度函数可以处理任意数量的进度指标和任意数量的工作线程。

如果您感兴趣的话,run_difficult_task中的一些内部循环变量(如jkl)可以添加到进度中。或者,您可以报告任何您想要的内容。


1
如果 run_difficult_task(i) 运行时间超过2秒,这个会更新吗? - Matsmath

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接