簡體   English   中英

即使不使用 RedirectStandardError/RedirectStandardOutput,Process.WaitForExit 也會掛起

[英]Process.WaitForExit hangs even without using RedirectStandardError/RedirectStandardOutput

我們有一個服務,它啟動一個進程並在服務停止時等待進程退出/服務用戶調用停止(停止/終止服務啟動的進程)。

偶爾, 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();
    }

}

不知道這是否可以回答您的問題(我自己的問題多於答案),但是這段代碼:

    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();
            }
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM