Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么调用Tesseract进程会导致此服务随机崩溃?_C#_Linux_Multithreading_.net Core_Tesseract - Fatal编程技术网

C# 为什么调用Tesseract进程会导致此服务随机崩溃?

C# 为什么调用Tesseract进程会导致此服务随机崩溃?,c#,linux,multithreading,.net-core,tesseract,C#,Linux,Multithreading,.net Core,Tesseract,我有一个运行在Ubuntu18.04虚拟机上的.NETCore2.1服务,通过一个进程实例调用TesseractOCR4.00。我想使用一个API包装器,但我只能找到一个可用的,而且它只在最新版本的Tesseract的测试版中——稳定的包装器使用版本3而不是4。在过去,这项服务工作得很好,但我一直在对其进行更改,以便减少从磁盘写入和读取文档/图像数据的频率,以提高速度。该服务用于调用更多的外部进程(如ImageMagick),由于存在API,这些进程是不必要的,因此我一直在用API调用替换这些进

我有一个运行在Ubuntu18.04虚拟机上的.NETCore2.1服务,通过一个进程实例调用TesseractOCR4.00。我想使用一个API包装器,但我只能找到一个可用的,而且它只在最新版本的Tesseract的测试版中——稳定的包装器使用版本3而不是4。在过去,这项服务工作得很好,但我一直在对其进行更改,以便减少从磁盘写入和读取文档/图像数据的频率,以提高速度。该服务用于调用更多的外部进程(如ImageMagick),由于存在API,这些进程是不必要的,因此我一直在用API调用替换这些进程

最近,我用一个从真实数据中提取的示例文件对此进行了测试。这是一个传真文档PDF,有133页,但由于灰度和分辨率的原因,只有5.8MB。该服务获取一个文档,将其拆分为各个页面,然后分配多个线程(每页一个线程)来调用Tesseract,并使用
Parallel.For
对其进行处理。线程限制是可配置的。我知道Tesseract有自己的多线程环境变量(OMP\u THREAD\u LIMIT)。在之前的测试中,我发现将其设置为“1”对于我们目前的设置是理想的,但是在我最近针对这个问题的测试中,我尝试将其设置为未设置(动态值),没有任何改进

问题是,调用Tesseract时,服务将挂起约一分钟,然后崩溃,journalctl中显示的唯一错误是:

dotnet[32328]: Error while reaping child. errno = 10
dotnet[32328]:    at System.Environment.FailFast(System.String, System.Exception)
dotnet[32328]:    at System.Environment.FailFast(System.String)
dotnet[32328]:    at System.Diagnostics.ProcessWaitState.TryReapChild()
dotnet[32328]:    at System.Diagnostics.ProcessWaitState.CheckChildren(Boolean)
dotnet[32328]:    at System.Diagnostics.Process.OnSigChild(Boolean)
我在网上找不到关于这个特定错误的任何信息。在我看来,基于我对
进程
类所做的相关研究,当进程退出并且dotnet试图清理它所使用的资源时,就会发生这种情况。我真的不知道如何处理这个问题,尽管我已经尝试了一些“猜测”,比如改变线程限制值。线程之间没有交叉。每个线程都有自己的页面分区(基于
并行方式。对于
对集合进行分区),并设置在这些页面上工作,一次一个

以下是从多个线程中调用的进程调用(8是我们通常设置的限制):

编辑4:经过与Clint在聊天中的大量测试和讨论,下面是我们学到的内容。该错误是由进程事件“OnSigChild”引发的,从堆栈跟踪中可以明显看出这一点,但无法钩住引发该错误的同一事件。如果超时时间为10秒,则进程不会超时(Tesseract处理给定页面通常只需几秒钟)。奇怪的是,如果进程超时被删除,我等待标准输出和错误流关闭,它将挂起20-30秒,但在挂起时间内进程不会出现在
ps auxf
中。据我所知,Linux能够确定进程已经完成,但.NET不能。否则,错误似乎是在进程执行完毕的那一刻出现的

对我来说,最令人困惑的事情仍然是,与我们在生产中使用的代码的工作版本相比,代码的流程处理部分实际上没有太大的变化。这表明这是我在某处犯的一个错误,但我根本找不到它。我想我必须在dotnetgithub跟踪器上提出一个问题

“获取子对象时出错”

进程占用内核中的一些资源,在Unix上,当父进程死亡时,是init进程负责清理内核资源,包括Zombine进程和孤立进程(也称为收获子进程)。NET核心在子进程终止时立即获取它们

“我发现删除stdout和stderr流ReadToEnd 调用会导致进程立即结束,而不是挂起,并且 同样的错误“

错误的原因是,您甚至在流程完成之前就提前调用了
p.ExitCode
,而使用
ReadToEnd
您只是在延迟此活动

更新代码摘要

  • StartInfo.FileName
    应指向要启动的文件名
  • UseShellExecute
    如果进程应直接从可执行文件创建,则为false;如果您希望在启动进程时使用shell,则为true
  • 向标准输出和错误流添加异步读取操作
  • AutoResetEvents
    在输出时发出信号,在操作完成时发出错误
  • Process.Close()
    释放资源
  • 设置和使用OverArguments属性更容易

修订模块


你确定这段代码可以编译吗,你的流程实例是
p
,我还看到你像这样调用ExitCode
process.ExitCode复制粘贴代码时出错,我现在会更正,谢谢您的回答。您的帖子让我对流程API有了很多了解,但不幸的是,在使用此代码时,我仍然在不一致的基础上收到相同的错误。看起来,即使使用超时,我仍然会在进程超时之前收到“Error while reaping child”,这会使dotnet崩溃并重新启动服务。我将不得不尝试几次,看看它是否与个别页面数据崩溃Tesseract有关,但它不应该相关,因为此文件在较旧版本的代码中可以正常运行。@LucasLeblanc,嗯,我希望您已经尝试了该代码段,并且没有漏掉任何一行,您还可以告诉我它会抛出哪一行吗“收获孩子时出错”。我们可以发起一次群聊,我很好奇,想知道到底是怎么回事。它没有被追踪到一行。异常在代码的其余部分“异步”发生。请检查
private bool ProcessOcrPage(IMagickImage page, int pageNumber, object instanceId)
        {
            var inputPageImagePath = Path.Combine(_fileOps.GetThreadWorkingDirectory(instanceId), $"ocrIn_{pageNumber}.{page.Format.ToString().ToLower()}");
            string outputPageFilePathWithoutExt = Path.Combine(_fileOps.GetThreadOutputDirectory(instanceId),
                    $"pg_{pageNumber.ToString().PadLeft(3, '0')}");
            page.Write(inputPageImagePath);

            var cmdArgs = $"-l eng \"{inputPageImagePath}\" \"{outputPageFilePathWithoutExt}\" pdf";
            bool success;

            _logger.LogStatement($"[Thread {instanceId}] Executing the following command:{Environment.NewLine}tesseract {cmdArgs}", LogLevel.Debug);

            var psi = new ProcessStartInfo("tesseract", cmdArgs)
            {
                RedirectStandardError = true,
                RedirectStandardOutput = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            // 0 is not the default value for this environment variable. It should remain unset if there 
            // is no config value, as it is determined dynamically by default within OpenMP.
            if (_processorConfig.TesseractThreadLimit > 0) 
                psi.EnvironmentVariables.Add("OMP_THREAD_LIMIT", _processorConfig.TesseractThreadLimit.ToString());

            using (var p = new Process() { StartInfo = psi })
            {
                string standardErr, standardOut;
                int exitCode;
                p.Start();
                standardOut = p.StandardOutput.ReadToEnd();
                standardErr = p.StandardError.ReadToEnd();        
                p.WaitForExit();
                exitCode = p.ExitCode;

                if (!string.IsNullOrEmpty(standardOut))
                    _logger.LogStatement($"Tesseract stdOut:\n{standardOut}", LogLevel.Debug, nameof(ProcessOcrPage));
                if (!string.IsNullOrEmpty(standardErr))
                    _logger.LogStatement($"Tesseract stdErr:\n{standardErr}", LogLevel.Debug, nameof(ProcessOcrPage));
                success = p.ExitCode == 0;
            }

            return success;
        }
private bool ProcessOcrPage(IMagickImage page, int pageNumber, object instanceId)
{
    StringBuilder output = new StringBuilder();
    StringBuilder error = new StringBuilder();
    int exitCode;
    var inputPageImagePath = Path.Combine(_fileOps.GetThreadWorkingDirectory(instanceId), $"ocrIn_{pageNumber}.{page.Format.ToString().ToLower()}");
        string outputPageFilePathWithoutExt = Path.Combine(_fileOps.GetThreadOutputDirectory(instanceId),
                $"pg_{pageNumber.ToString().PadLeft(3, '0')}");
    page.Write(inputPageImagePath);

    var cmdArgs = $"-l eng \"{inputPageImagePath}\" \"{outputPageFilePathWithoutExt}\" pdf";
    bool success;

    _logger.LogStatement($"[Thread {instanceId}] Executing the following command:{Environment.NewLine}tesseract {cmdArgs}", LogLevel.Debug);


    using (var outputWaitHandle = new AutoResetEvent(false))
    using (var errorWaitHandle = new AutoResetEvent(false))
    {
        try
        {
            using (var process = new Process())
            {
                process.StartInfo = new ProcessStartInfo
                { 
                    WindowStyle = ProcessWindowStyle.Hidden,
                    FileName = "tesseract.exe", // Verify if this is indeed the process that you want to start ?
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    Arguments = cmdArgs,
                    WorkingDirectory = Path.GetDirectoryName(path)
                };



                if (_processorConfig.TesseractThreadLimit > 0) 
                    process.StartInfo.EnvironmentVariables.Add("OMP_THREAD_LIMIT", _processorConfig.TesseractThreadLimit.ToString());


                process.OutputDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        outputWaitHandle.Set();
                    }
                    else
                    {
                        output.AppendLine(e.Data);
                    }
                };
                process.ErrorDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        errorWaitHandle.Set();
                    }
                    else
                    {
                        error.AppendLine(e.Data);
                    }
                };

                process.Start();

                process.BeginOutputReadLine();
                process.BeginErrorReadLine();

                  if (!outputWaitHandle.WaitOne(ProcessTimeOutMiliseconds) && !errorWaitHandle.WaitOne(ProcessTimeOutMiliseconds) && !process.WaitForExit(ProcessTimeOutMiliseconds))
                  {
                    //To cancel the read operation if the process is stil reading after the timeout this will prevent ObjectDisposeException
                    process.CancelOutputRead();
                    process.CancelErrorRead();

                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("Timed Out");

                    //To release allocated resource for the Process
                    process.Close();
                    //Timed out
                    return  false;
                  }

                  Console.ForegroundColor = ConsoleColor.Green;
                  Console.WriteLine("Completed On Time");

                 exitCode = process.ExitCode;

                  if (!string.IsNullOrEmpty(standardOut))
                    _logger.LogStatement($"Tesseract stdOut:\n{standardOut}", LogLevel.Debug, nameof(ProcessOcrPage));
                  if (!string.IsNullOrEmpty(standardErr))
                    _logger.LogStatement($"Tesseract stdErr:\n{standardErr}", LogLevel.Debug, nameof(ProcessOcrPage));

                 process.Close();

                 return exitCode == 0 ? true : false;
            }
        }
        Catch
        {
           //Handle Exception
        }
    }
}