【问题标题】:Dispose (or Close) killed process处置(或关闭)被杀死的进程
【发布时间】:2021-01-12 18:05:01
【问题描述】:

process.Kill() 之后调用process.Dispose()process.Close()(不管是哪一个,在.Dispose 实现中调用'cos .Close)有时会挂起应用程序。

我无法稳定地重现此错误,但有时当WaitForExit 通过超时完成时,应用程序会在process.Close() 命令上挂起。

请建议我,这个问题的原因可能是什么?

注意:

  • 我见过similar question。但是没有答案和赞成的评论说,问题的原因可能在细节上,在那个问题上没有提供。所以我添加了更多细节。
  • 我也见过linked solution,但我不能使用ProcessStartInfo.UseShellExecute = true;,因为我需要重定向输出。

对不起,冗长的代码,但我是这样传递的,因为在类似的未回答的问题评论员注意到,没有提供足够的细节(如上所述)

private static async Task<int> RunMethod(string processArguments)
{
    // 1. Prepare ProcessStartInfo

    ProcessStartInfo startInfo = new ProcessStartInfo();

    startInfo.Arguments = processArguments;
    startInfo.RedirectStandardOutput = true;
    startInfo.RedirectStandardError = true;
    startInfo.CreateNoWindow = true;
    startInfo.UseShellExecute = false;

    // 2. Create process inside using-block to be disposed

    using (var proc = new Process())
    {
        proc.StartInfo = startInfo;

        // 3. Subscribe output streams handlers

        proc.OutputDataReceived += (sender, outputLine) => { HandleMessage(outputLine); };
        proc.ErrorDataReceived  += (sender, errorLine)  => { HandleMessage(errorLine); };

        // 4. Start process

        if (!proc.Start())
        {
            proc.Close();
            return -1;
        }

        // 5. Start the asynchronous read of the standard output stream.

        proc.BeginOutputReadLine();
        proc.BeginErrorReadLine();

        // 6. Waiting process to exit with timeout on threadpool to not block UI thread

        // Re#er warns me "captured variable `proc` is disposed in the outer scope". But I think it's Ok, 'cos we're awaiting this task inside using block (in next line)
        var waitingProcessTask = Task.Run(() => proc.WaitForExit(TIMEOUT), _cancelToken);
        bool hasExited = await waitingProcessTask;

        // 7. Stop reading streams

        // Not sure, these 2 CalncelXxxRead methods are needed. But hope it won't hurt at least
        proc.CancelErrorRead();
        proc.CancelOutputRead();

        // 8. If !hasExited (i.e. TIMEOUT is reached) we kill the process

        if (!hasExited)
        {
            Logger.Debug("0. Before Kill()");

            proc.Kill();
            proc.Refresh(); // not sure, it's needed
        }

        // If uncomment next 2 lines, then problem moves here from end of using block
        //proc.Close();
        //Logger.Debug("1. after .Close call");  // <------------------ This log we don't see sometimes
        
        Logger.Debug("2. last inside using-block");
    } // end of using block

    Logger.Debug("3. after using-block");  // <------------------ This log we don't see sometimes (if `.Close` wasn't called before)
    
    return 0;
}

【问题讨论】:

  • 问题不是处理本身,而是异步流读取行功能导致竞争条件。阅读Process 上的文档,它有很多关于此的警告——如果你使用它们,你有责任编写正确的代码或冻结你的线程。

标签: c# async-await process dispose kill-process


【解决方案1】:

我已经解决了我的问题。问题是:我的进程偶尔会产生子守护进程,它可以永远工作。如果父流被重定向,子进程(按设计)总是继承重定向的输出流。

另一方面,.Kill() 不会杀死子进程 - 仅针对它被终止的子进程。 (直到 .Net 5.0,.Kill(bool) 解决了这个问题)。

所以.Dispose().Close() 永远不会完成,'cos 等待释放由无限子进程持有的输出流。

弄清楚发生了什么是非常有趣的冒险=)


**) - 这就是为什么重现非常不稳定

PS:感谢@Blindy 为我提供了非常接近问题真正原因所在的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-19
    • 1970-01-01
    • 1970-01-01
    • 2019-04-14
    • 2018-03-18
    • 2010-12-08
    • 1970-01-01
    • 2013-04-07
    相关资源
    最近更新 更多