我们有一个服务启动一个进程并等待进程在服务停止/服务调用用户停止时退出(停止/终止服务启动进程)。
偶尔会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();
}
}
答案 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();
}
}
}