提问人:Hiran Patel 提问时间:5/7/2018 最后编辑:Hiran Patel 更新时间:5/26/2020 访问量:714
即使不使用 RedirectStandardError/RedirectStandardOutput,Process.WaitForExit 也会挂起
Process.WaitForExit hangs even without using RedirectStandardError/RedirectStandardOutput
问:
我们有一个服务,当服务停止/服务调用的用户停止(停止/终止服务启动的进程)时,它启动一个进程并等待进程退出。
偶尔挂起。process.waitForExit(TimeSpan)
请注意,Service 启动的进程是本机进程 (C++/CLI) 进程,而 Service 是 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();
}
}
答:
0赞
FandangoOnCore
5/26/2020
#1
不知道这是否可以回答您的问题(我自己对此的问题多于答案),但是此代码:
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();
}
}
}
评论
new ProcessStartInfo(_exePath, arguments) { RedirectStandardOutput ,};