【问题标题】:What is the C way to report progress of computation? [closed]报告计算进度的 C 方法是什么? [关闭]
【发布时间】:2016-05-05 19:56:42
【问题描述】:

这是Using a thread in C++ to report progress of computations 的后续问题。

假设我有一个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 循环和报告并行机制。


相关:How to "multithread" C codeHow do I start threads in plain C?

【问题讨论】:

  • 让它报告给不同的进程而不是不同的线程不是更容易吗?

标签: c multithreading pthreads


【解决方案1】:

Linux(以及 POSIX 系统)提供 alarm 库调用。这使您可以在几秒钟后执行某些操作,而不会中断主线程,并且在您不需要多线程时也不会打扰它。它非常适合像您这样的用例。

【讨论】:

  • 我一定会去看看的。
  • 好建议。但是,OP 应该注意,以这种方式解决问题需要run_difficult_task() 对信号中断具有鲁棒性。这是否是个大问题取决于该函数的细节。
  • 另一种选择几乎可以肯定是让run_difficult_task() 以某种方式在外部传达其进度(消息传递等)。 run_difficult_task() 也可以修改为将低开销存储到充当环形缓冲区的内存支持文件中,这将允许外部仪器在运行时监控进度。最后,可以使用gdbperf 之类的工具随时间对进程状态进行采样。
【解决方案2】:

您可以尝试使用一个线程(工作线程)或两个线程(一个执行计算,一个在 main 执行其他操作或只是等待时显示输出)和一些全局变量(呃)。

第一个线程将是你的主力进行计算和更新一些全局变量。第二个(可能只是主线程)将检查该变量是否已更改,然后打印统计信息(也许该变量将保存统计信息,例如百分比)。


你可以尝试什么:

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;

【讨论】:

  • 您不一定需要全局变量来实现基于线程的解决方案,但您确实需要共享变量。如果您要实现一个涉及共享变量的解决方案,那么您还需要适当的同步。在这种情况下,这可能意味着通过互斥锁保护对共享变量的访问。
  • 如果我理解正确,如果只有一个线程,那么您实际上是在建议将报告机制(例如 printf)inside run_difficult_task()?这没有多大帮助。
  • @Matsmath,不,你没有正确理解。 工作线程是你的run_difficult_taskprintf 位于 '// in main thread'
  • 啊,那好吧。因此,澄清一下,当您说“您可以尝试使用一个线程”时,您实际上是指“您可以尝试使用除主线程之外的另一个(=worker)线程”。所以至少有两个线程。
  • @Matsmath,是的。我对这部分进行了一些改写,所以你可能想看看编辑。
【解决方案3】:

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

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 来定义的,这是由外循环完成的。

对于我自己的东西,progress 函数可以处理来自任意数量的工作线程的任意数量的进度指示器。

所以,如果您对此感兴趣,并且 [say] run_difficult_task 有一些内部循环变量,例如 jkl,这些可以添加到进度中。或者,希望报告的任何内容。

【讨论】:

  • 如果run_difficult_task(i) 运行时间超过 2 秒,是否会更新?
猜你喜欢
  • 1970-01-01
  • 2010-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-11
  • 1970-01-01
相关资源
最近更新 更多