即使不使用RedirectStandardError / RedirectStandardOutput,Process.WaitForExit也会挂起

时间:2018-05-07 11:04:38

标签: c# c++-cli

我们有一个服务启动一个进程并等待进程在服务停止/服务调用用户停止时退出(停止/终止服务启动进程)。

偶尔会process.waitForExit(TimeSpan)挂起。

请注意,Service启动的进程是本机进程(C ++ / CLI)进程,服务是C#。

以下是我们正在使用的代码段

public class ApplicationProcessControl : IProcessControl
 {  
    private Process _proc;
    private const int ProcessIdleTimeout = 5000;

    public bool Start(string arguments)
    {
        if (IsAlive)
        {
            Log.TraceInfo("Application process already running. Killing it now...");
            _proc.Kill();
        }

        var eProcStarted = new Mutex(false, "Mutex111");

        _proc = new Process { EnableRaisingEvents = true, StartInfo = new ProcessStartInfo(_exePath, arguments) { RedirectStandardOutput = false,RedirectStandardError = false};
        _proc.Exited += OnProcessExited;

        _proc.Start();
        bool started;
        if(_proc == null)
        {
            Log.TraceInfo("Unable to start application process");
            started = false;
        }
        else
        {
            started = eProcStarted.WaitOne(ProcessIdleTimeout);

            if(started)
            {
                Log.TraceInfo($"Application process with id {_proc.Id} started successfully");
            }
        }
        eProcStarted.Dispose();
        return started;
    } 

    public void Kill()
    {
        _proc.Kill();
    }

    public bool WaitForProcessToExit(TimeSpan timeout)
    {
        return _proc.WaitForExit((int) timeout.TotalMilliseconds);
    }

    public event Action ProcessExited;

    private void OnProcessExited(object sender, EventArgs e)
    {
        var proc = sender as Process;

        if(proc != null)
        {
            proc.Exited -= OnProcessExited;

            if(proc.ExitCode == 0)
            {
                Log.TraceInfo("Application process exited gracefully");
            }
            else
            {
                Log.DeveloperWarning("Application process exited unexpectedly with code {0}", proc.ExitCode);
                OnProcessExited();
            }
        }
    }

    private void OnProcessExited()
    {
        Action handler = ProcessExited;
        handler?.Invoke();
    }
}

public interface IProcessControl
{
    bool IsAlive { get; }

    bool Start(string arguments);

    bool WaitForProcessToExit(TimeSpan timeout);

    void Kill();

    event Action ProcessExited;
}

public class ApplicationClientService: DisposableObject, IComponentService, ITaskControl, IUIControl,
        IDataProvider<AngleFlavors>, IApplicationCloseNotifier
{
    //...
    private readonly IProcessControl _procCtrl;

    public ApplicationClientService(IObjectProvider objPro)
    {
        //...
        _procCtrl.ProcessExited += OnApplicationProcessExited;              
    }

    public void Stop()
    {
        //...
        CleanUpAppProcess();
        //...
    }


    private void CleanUpAppProcess()
    {
        //...

        if(!_procCtrl.WaitForProcessToExit(TimeSpan.FromSeconds(5)))
        {
            _procCtrl.Kill();
        }
    }

    private void OnApplicationProcessExited()
    {
        if(!_isAppRunning)
        {
            return;
        }

        _isAppRunning = false;
        _autoLaunchRequested = false;
        RaiseApplicationClosed();
        Log.DeveloperWarning("Application process closed unexpectedly");
        Log.UserMessageApplicationClosedUnexpectedly();
        ...
    }

    protected virtual void RaiseApplicationClosed()
    {
        //AuditApplicationStop();
        //ApplicationClosed?.Invoke();
    }

}

1 个答案:

答案 0 :(得分:0)

不知道这是否可以回答您的问题(我自己比这有更多的问题),但是代码如下:

    private void CleanUpAppProcess()
    {
        //...

        if(!_procCtrl.WaitForProcessToExit(TimeSpan.FromSeconds(5)))
        {
            _procCtrl.Kill();
        }
    }

在Kill命令之前调用WaitForExit。您是否希望该过程自己终止/由用户在5秒内终止?正如Bennie Zhitomirsky在他的评论中指出的,互斥体在应有的时候不归所有人所有(如果我正确理解了您想要实现的目标,如果不能,抱歉)。那么IsAlive的实现又如何呢?

无论如何,我为ApplicationProcessControl类添加了几行。我只是用一些本机过程对其进行了测试,并且似乎可以正常工作(但是,仍然不确定这是您要达到的目标):

    public class ApplicationProcessControl : IProcessControl
    {
        /// <summary>
        /// Process instance variable.
        /// </summary>
        private Process _proc;
        /// <summary>
        /// The thread will try to acquire the mutex for a maximum of _mutexAcquireTimeout ms.
        /// </summary>
        private const int _mutexAcquireTimeout = 5000;
        /// <summary>
        /// Global static named mutex, seen by all threads.
        /// </summary>
        private static Mutex SpawnProcessMutex = new Mutex(false, "Mutex111");
        /// <summary>
        /// The state of the process.
        /// </summary>
        public bool IsAlive
        {
            get { return !(_proc is null) && !_proc.HasExited; }
        }
        /// <summary>
        /// Spawns a new process.
        /// </summary>
        public bool Start(string arguments)
        {
            // Try to acquire the mutex for _mutexAcquireTimeout ms.
            if (!SpawnProcessMutex.WaitOne(_mutexAcquireTimeout) || IsAlive)
            {
                // Mutex is still owned (another thread got it and is trying to run the process) 
                // OR the process is already running.
                // DO NOT start a new process.
                return false;
            }

            try
            {
                // Mutex is acquired by this thread.
                // Create a new instance of the Process class.
                _proc = new Process
                {
                    EnableRaisingEvents = true,
                    StartInfo = new ProcessStartInfo("the_process_to_be_run", arguments)
                    {
                        RedirectStandardOutput = false,
                        RedirectStandardError = false
                    }
                };
                // Subscription to the ProcessExited event.
                _proc.Exited += OnProcessExited;
                // Run the process.
                var haveAnHandle = _proc.Start();

                // *******
                // TODO: The process started but we may not have an handle to it. What to do?
                // *******

                //Log.TraceInfo($"Application process with id {_proc.Id} started successfully");
                return true;
            }
            catch (Exception) // TODO: [Catch the specific exceptions here]
            {
                // The process failed to start, still we have an instance of Process with no use.
                if (!(_proc is null))
                {
                    _proc.Dispose();
                    _proc = null;
                }
                //Log.TraceInfo("Unable to start application process");
                return false;
            }
            finally 
            {
                // Release the mutex, another thread may be waiting to acquire it.
                SpawnProcessMutex.ReleaseMutex();
            }
        }
        /// <summary>
        /// Kills the process started by the Start method.
        /// </summary>
        public void Kill()
        {
            if (IsAlive) _proc.Kill();
        }
        /// <summary>
        /// Can't see a real reason to block the thread waiting synchronously for the process to
        /// exit, we are already subscribed to the Exited event.
        /// Kept here to avoid breaking the interface contract.
        /// </summary>
        public bool WaitForProcessToExit(TimeSpan timeout)
        {
            return _proc.WaitForExit((int)timeout.TotalMilliseconds);
        }
        /// <summary>
        /// Client class consumable event to know the the process actually terminated.
        /// </summary>
        public event Action ProcessExited;
        /// <summary>
        /// Process Exited event handler.
        /// </summary>
        private void OnProcessExited(object sender, EventArgs e)
        {
            // Get a reference to the actual Process object.
            var proc = sender as Process;
            if (proc is null) return;

            proc.Exited -= OnProcessExited;

            if (proc.ExitCode == 0)
            {
                // Log.TraceInfo("Application process exited gracefully");
            }
            else
            {
                // Log.DeveloperWarning("Application process exited unexpectedly with code {0}", proc.ExitCode);
                ProcessExited?.Invoke();
            }
        }
    }