Process Creation and Coordination
In some previous posts (Out of Process IPC/TCP Remoting code, .Net Remoting (AppDomains, Out of Process, Two Way, etc..)) I showed some sample code using .Net remoting over a process boundary. As a result, I received a few questions about the process creation and how to handle the process coordination (i.e., The Host/Master process waiting on the created process startup). I apologize to those who have been waiting for another sample on the subject, but I have been pretty busy as of late.
The attached sample code shows the following;
1. How to ensure the Host Process waits on the new process creation.
a. Previously I showed a simple *hack* where the created process sleeps for a while in order to let the process creation complete.
b. A much cleaner and more reliable solution uses EventWaitHandle.
2. The created Process runs until the Host Process is terminated or the Host terminates the new process.
a. Previously I showed a simple *hack* where the created process does a Console.ReadLine() in order to halt code execution and thus cause process termination.
b. I didn’t show an example of how the created process can shutdown in the event the Host process shuts down. This scenario covers orphaned processes.
c. The solution: System.Diagnostics.Process.GetProcessById(HostPID).WaitForExit();
View Source for Host Process: Show Code ...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Threading;
namespace SampleWaitForProcessStartup
{
class Program
{
static void Main(string[] args)
{
String CurrentProcessInfo = String.Format(CultureInfo.InvariantCulture
, "Host Process:{0}, PID:{1}"
, Process.GetCurrentProcess().ProcessName
, Process.GetCurrentProcess().Id.ToString());
Console.WriteLine(CurrentProcessInfo + ", Running.");
int TIMEOUT_SECONDS = 5;
Guid GUID = Guid.NewGuid();
String NewProcessArgs = String.Format(CultureInfo.InvariantCulture
, "{0} {1}"
, GUID.ToString()
, Process.GetCurrentProcess().Id.ToString());
Process NewProcess = new Process();
NewProcess.StartInfo.FileName = Environment.CurrentDirectory + "\\SampleProcess.exe";
NewProcess.StartInfo.CreateNoWindow = false;
NewProcess.StartInfo.Arguments = NewProcessArgs;
NewProcess.StartInfo.UseShellExecute = false;
// Wait until the process we are creating is ready.
// Appending a unique identifier to the external process handle which also aids in debugging.
EventWaitHandle readyEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "SampleProcess:" + GUID.ToString());
NewProcess.Start();
// wait nSeconds for the process to start
bool success = readyEvent.WaitOne(new TimeSpan(0, 0, TIMEOUT_SECONDS), false);
readyEvent.Close();
if (!success)
{
// Try to avoid leaving a half-baked process around if possible.
try
{
NewProcess.Kill();
}
catch (Exception) { }
}
Console.WriteLine("Press <Enter> to shut down the Host Process and in turn the New/Created process should shut down.");
Console.ReadLine();
}
}
}
View Source for Created Process: Show Code ...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Threading;
namespace SampleProcess
{
class Program
{
/// <summary>
/// This is a sample console app that is started up by a Host process (e.g., "SampleWaitForProcessStartup").
/// It is difficult to ensure that a managed process (i.e., this app) is started completely by a Host app/process
/// before communicating between the processes. Most people use a less determisitic aproach by putting a
/// sleep timer in the process they are starting up.
/// </summary>
/// <param name="args">
/// Arg1: A unique identifier of this process. The parm is passed on the command line to
/// this process that is started.
/// Arg2: PID of the Host Process. In the event the Host process shuts down, so whould we.</param>
static int Main(string[] args)
{
TimeSpan waitTime = new TimeSpan(0, 0, 10);
// We will use the convention that a process exit value of 0 == sucess.
// This also allows command line tasks to "catch a bad process exit".
if (args == null || args.Length != 2 || args[0].Length != 36)
return 1;
string GUID = args[0];
int HostPID = Convert.ToInt32(args[1], CultureInfo.InvariantCulture);
// Prepend "ProcessName" to unique identifier (i.e., per process) for wait handle. This is
// also handy when debugging.
// If you attach a VS debugger to a running process, then press the pause icon,
// you can see local variables and the GUID that was passed to help identify the spawned process.
string CurrentProcessName = System.Diagnostics.Process.GetCurrentProcess().ProcessName + ":" + GUID;
String CurrentProcessInfo = String.Format(CultureInfo.InvariantCulture, "Process:{0}, PID:{1}"
, CurrentProcessName, Process.GetCurrentProcess().Id.ToString());
Console.WriteLine(CurrentProcessInfo + ", Starting.");
// Signal to the host process that we are ready for communication.
EventWaitHandle readyEvent = new EventWaitHandle(false, EventResetMode.ManualReset, CurrentProcessName);
// Uncomment out the next line in order to test killing this process if it does not startup within
// the Host process requested timeout.
//Thread.Sleep(waitTime);
readyEvent.Set();
readyEvent.Close();
Console.WriteLine(CurrentProcessInfo + ", Started.");
try
{
Console.WriteLine("Waiting on Host Process PID (" + HostPID.ToString() + ") to exit.");
System.Diagnostics.Process.GetProcessById(HostPID).WaitForExit();
}
catch
{
// The host may have shut down before we could wait on it.
Environment.Exit(1);
return 1;
}
Environment.Exit(0);
return 0;
}
}
}
Comments
- Anonymous
May 29, 2009
PingBack from http://paidsurveyshub.info/story.php?title=jack-gudenkauf-jackg-weblog-process-creation-and-coordination