【问题标题】:What can cause non-deterministic output in a program?什么会导致程序中的非确定性输出?
【发布时间】:2009-06-06 19:29:52
【问题描述】:

我在多进程程序中有一个错误。该程序接收输入并立即产生输出,不涉及网络,也没有任何时间参考。 导致此错误的原因难以追查的原因是它只发生有时

如果我不断运行它,它会产生正确和错误的输出,没有可辨别的顺序或模式。

什么会导致这种不确定的行为?有没有可以提供帮助的工具?有可能存在未初始化的变量。我如何找到这些?

编辑:问题已解决,感谢任何建议的人 比赛条件。 我没有想到它主要是因为我确信我的设计可以防止这种情况发生。问题是我使用了 'wait' 而不是 'waitpid',因此有时,当某个进程幸运地在我期望的进程之前完成时,事情的正确顺序就会变得疯狂。

【问题讨论】:

    标签: c linux debugging deterministic


    【解决方案1】:

    你说这是一个“多进程”程序——你能说得更具体些吗?这很可能是您如何处理多个进程的竞争条件。

    如果您能告诉我们更多有关流程如何交互的信息,我们或许能够提出一些可能性。请注意,尽管 Artem 建议使用调试器本身很好,但您需要注意,引入调试器很可能会完全改变这种情况 - 特别是在涉及竞争条件时。就我个人而言,我非常喜欢记录日志,但即使这样也可以巧妙地改变时间。

    【讨论】:

      【解决方案2】:

      调度器!

      基本上,当您有多个进程时,它们可以按照他们想要的任何奇怪的顺序运行。如果这些进程共享它们正在读取和写入的资源(无论是文件、内存还是某种 IO 设备),那么操作将以各种奇怪的顺序交错。举个简单的例子,假设你有两个线程(它们是线程,所以它们共享内存)并且它们都试图增加一个全局变量 x。

      y = x + 1;
      x = y
      

      现在运行这些进程,但以这种方式交错代码

      假设 x = 1

      P1:

      y = x + 1
      

      所以现在在 P1 中,对于变量 y,它是本地的并且在堆栈上,y = 2。然后调度器进来并启动P2

      P2:

      y = x + 1
      x = y
      

      x 仍然是 1,所以添加了 1,现在是 x = 2

      然后P1结束

      P1:

      x = y
      

      x 仍然是 2!我们将 x 增加了两次,但只得到了一次。由于我们不知道如何这会发生,它被称为非确定性行为。

      好消息是,您偶然发现了系统编程中最困难的问题之一,也是许多函数式语言人员的主要战斗口号。

      【讨论】:

        【解决方案3】:

        您很可能正在查看race condition,即不可预测的,因此难以重现和调试不正确同步的线程或进程之间的交互。

        这种情况下的不确定性源于进程/线程和内存访问调度。这是不可预测的,因为它受到大量外部因素的影响,包括网络流量和用户输入,这些因素不断导致中断并导致程序每次运行时线程中的实际执行顺序不同。

        【讨论】:

          【解决方案4】:

          这可能是很多事情,内存泄漏,关键部分访问,未关闭的资源,未关闭的连接等等。只有一个工具可以帮助您 - 调试器,或者尝试检查您的算法并找到错误,或者如果您成功指出有问题的部分,您可以在这里粘贴一个sn-p,我们会尽力帮助您。

          【讨论】:

          • 很好的嫌疑人名单,但不是很好,只列出一个工具。还可以考虑 valgrind、错误检查 malloc 替换(如 efence)等
          【解决方案5】:

          从基础开始...确保所有变量都具有默认值,并且在使用之前将所有动态内存清零(即使用 calloc 而不是 malloc)。应该有一个编译器选项来标记这个(除非你使用一些晦涩的编译器)。

          如果这是 c++(我知道它应该是一个 'c' 论坛),有时对象创建和初始化滞后于变量赋值可能会咬你。例如,如果您有一个由多个线程同时使用的范围(如在单例或全局变量中),这可能会导致问题:

          如果 (!foo) Foo tmp = new Foo();

          如果您有多个线程访问上述内容,则第一个线程会找到 foo = null 并开始对象创建和分配,然后产生。另一个线程进来并发现 foo != null 所以跳过该部分并开始使用 foo。

          【讨论】:

            【解决方案6】:

            我们需要查看您的代码的细节才能给出更准确的答案,但简而言之,当您有一个在多个进程或多个线程之间进行协调的程序时,线程执行时间的变量可以为您的应用程序添加不确定性。本质上,操作系统所做的调度会导致进程和线程乱序执行。根据您的环境和代码,操作系统所做的调度可能会导致截然不同的结果。您可以在 google 上搜索有关使用多线程进行乱序执行的更多信息以获取更多信息;这是一个很大的话题。

            【讨论】:

              【解决方案7】:

              “多进程”是指多线程吗?如果我们有两个线程来执行这个例程

              i = 1;
              while(true)
              {
                  printf(i++);
                  if(i > 4) i = 1;
              }
              

              通常我们希望输出类似于

              112233441122334411223344
              

              但实际上我们会看到类似的东西

              11232344112233441231423
              

              这是因为每个线程会以不同的速率使用 CPU。 (日程安排背后有很多复杂的东西,我太弱了,无法告诉你背后的技术内容。)可以说,从普通人的角度来看,日程安排是非常随机的。

              这是其他 cmets 中提到的竞争条件示例。

              【讨论】:

                猜你喜欢
                • 2013-07-04
                • 2021-12-26
                • 1970-01-01
                • 2020-09-02
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多