Solved

Create child process in C#

Posted on 2014-10-14
8
1,964 Views
Last Modified: 2014-10-15
Hi Experts,

Lets me describe. Currently what we have is that an C# application console that run bat files sequential. The console app just creates a process to run a bat file. It waits for the bat file executes until complete and then runs the other ones. Please notice that all the bat file with same name 1.bat

Here is the code:
 foreach(string clientDirectory in clientDirectories)
                    {
                        System.IO.Directory.SetCurrentDirectory(clientDirectory);
                        if (System.IO.File.Exists(System.IO.Path.Combine(clientDirectory, "1.bat")))
                        {
                            ExecuteCommand(System.IO.Path.Combine(clientDirectory, "1.bat"));
                        }
                    }


static void ExecuteCommand(string command)
        {
            int exitCode;
            ProcessStartInfo processInfo;
            Process process;

            processInfo = new ProcessStartInfo("cmd.exe", "/c \"" + command + "\"");
            processInfo.CreateNoWindow = true;
            processInfo.UseShellExecute = false;
            // *** Redirect the output ***
            processInfo.RedirectStandardError = true;
            processInfo.RedirectStandardOutput = true;

            process = Process.Start(processInfo);
            process.WaitForExit();

            // *** Read the streams ***
            string output = process.StandardOutput.ReadToEnd();
            string error = process.StandardError.ReadToEnd();

            exitCode = process.ExitCode;

            Console.WriteLine(System.IO.Directory.GetCurrentDirectory());
            Console.WriteLine("output>>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
            Console.WriteLine("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
            Console.WriteLine("ExitCode: " + exitCode.ToString(), "ExecuteCommand");
            process.Close();
        }

Open in new window


Now I want to change from running bat files sequentially to parallel. What I think is that the console C# app creates multiple parallel processes. Once child process finishes his task, he closes himself or notifies parent process. It should be great if console app could check current machine has enough memory available or not before creating new process (Let say allocate static 200MB for each).

I am actually research on that but it would shorten my time if experts put some thoughts here.

Thanks in advance,
Dave
0
Comment
Question by:David_Duong
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 3
  • 2
8 Comments
 
LVL 40

Expert Comment

by:Kyle Abrahams
ID: 40381023
The below should work for executing different threads.   As for checking the different memory I'm not sure about.


using System.Threading;

{
List<thread> threads = new List<threads>();
foreach(string clientDirectory in clientDirectories)
                    {
                       Thread t = new Thread(new ThreadStart(processDIr));
                       t.Start(clientDirectory);
                        threads.add(t);
                    }

//wait until all threads no in background or running state.  You may need to add others.
while (threads.Where(r => r.ThreadState == ThreadState.Background || r.ThreadState == ThreadState.Running).Count() > 0)
   Thread.Sleep(1000);

}

private void processDIr(object Dir)
{  
   string clientDirectory = Dir.ToString();

                        System.IO.Directory.SetCurrentDirectory(clientDirectory);
                        if (System.IO.File.Exists(System.IO.Path.Combine(clientDirectory, "1.bat")))
                        {
                            ExecuteCommand(System.IO.Path.Combine(clientDirectory, "1.bat"));
                        }
}

//ExecuteCommand remains untouched.

Open in new window

0
 
LVL 34

Expert Comment

by:it_saige
ID: 40381083
As opposed to using a Thread.Sleep(x); I would recommend using Events to signal with the Thread has completed; e.g. -
using System;
using System.Collections.Generic;
using System.Threading;

namespace CSharpThreadExampleExample
{
	public class Worker
	{
		#region ThreadWorderCompleted Memebers
		[NonSerialized]
		private ThreadWorkerCompletedEventHandler _threadWorkerCompleted;
		public event ThreadWorkerCompletedEventHandler ThreadWorkerCompleted
		{
			add { _threadWorkerCompleted += value; }
			remove { _threadWorkerCompleted -= value; }
		}

		protected virtual void OnThreadWorkerCompleted(object sender, ThreadWorkerCompletedEventArgs e)
		{
			ThreadWorkerCompletedEventHandler handler = _threadWorkerCompleted;
			if (handler != null)
				handler(sender, e);
		}
		#endregion

		private bool shouldStop = false;
		private int id = -1;
		public int ID
		{
			get { return id; }
			private set 
			{ 
				if (!value.Equals(id))
					id = value;
			}
		}

		public void DoWork()
		{
			int count = 0;
			while (!shouldStop)
			{
				Console.WriteLine(string.Format("Worker Thread {0}: Working ({1} passes)...", ID, count + 1));
				// Simulating long running process.
				Thread.Sleep(200);
				count++;
				if (count == 5)
					shouldStop = true;
			}
			Console.WriteLine(string.Format("Worker Thread {0}: Terminating gracefully", ID));
			OnThreadWorkerCompleted(this, new ThreadWorkerCompletedEventArgs(ID));
		}

		public void RequestStop()
		{
			shouldStop = true;
		}

		private Worker() { ;}

		public Worker(int ID)
		{
			id = ID;
		}
	}

	class Program
	{
		static AutoResetEvent reset = new AutoResetEvent(false);
		static List<Worker> workers = new List<Worker>();
		static bool creatingThreads = false;

		static void Main(string[] args)
		{
			creatingThreads = true;
			for (int i = 0; i < 5; i++)
				CreateAndStartThreads(i + 1);
			Console.WriteLine("Settings the reset event to block the main thread");
			reset.WaitOne();
			Console.WriteLine("Finished executing all threads.");
			Console.ReadLine();
		}

		public static void CreateAndStartThreads(int ID)
		{
			Worker worker = new Worker(ID);
			worker.ThreadWorkerCompleted += OnThreadWorkerCompleted;
			Thread thread = new Thread(worker.DoWork) { Priority = ThreadPriority.Normal, IsBackground = true };
			thread.Start();
			Console.WriteLine(string.Format("Adding worker with ID - {0}", ID));
			workers.Add(worker);

			if (ID == 5)
				creatingThreads = false;
		}

		public static void OnThreadWorkerCompleted(object sender, ThreadWorkerCompletedEventArgs e)
		{
			if (sender is Worker)
			{
				Console.WriteLine(string.Format("Removing worker with ID - {0}", (sender as Worker).ID));
				workers.Remove(sender as Worker);
				if (!creatingThreads && workers.Count == 0)
					reset.Set();
			}
		}
	}

	public delegate void ThreadWorkerCompletedEventHandler(object sender, ThreadWorkerCompletedEventArgs e);

	public class ThreadWorkerCompletedEventArgs : EventArgs
	{
		// Ideally you really want to use items that can identify the thread.
		public int ID { get; private set; }

		private ThreadWorkerCompletedEventArgs()
		{
			ID = default(int);
		}

		public ThreadWorkerCompletedEventArgs(int ID)
		{
			this.ID = ID;
		}
	}
}

Open in new window

I would also say look into using a QueueUserWorkItem (part of the ThreadPool class) as opposed to a standard thread model.  This is because being a part of a thread pool, the QueueUserWorkItem waits until a thread in the thread pool becomes available before consuming it.  This would allow for you to allocate resources and such depended upon your thread pool configuration.

http://msdn.microsoft.com/en-us/library/system.threading.threadpool.queueuserworkitem(v=vs.110).aspx

-saige-
0
 
LVL 34

Expert Comment

by:it_saige
ID: 40381107
Here is the above example, modified to use a QueueUserWorkItem:
using System;
using System.Collections.Generic;
using System.Threading;

namespace QueueUserWorkItemExample
{
	public class QueueWorker
	{
		#region QueueWorderCompleted Memebers
		[NonSerialized]
		private QueueWorkerCompletedEventHandler _queueWorkerCompleted;
		public event QueueWorkerCompletedEventHandler QueueWorkerCompleted
		{
			add { _queueWorkerCompleted += value; }
			remove { _queueWorkerCompleted -= value; }
		}

		protected virtual void OnQueueWorkerCompleted(object sender, QueueWorkerCompletedEventArgs e)
		{
			QueueWorkerCompletedEventHandler handler = _queueWorkerCompleted;
			if (handler != null)
				handler(sender, e);
		}
		#endregion

		private bool shouldStop = false;
		private int id = -1;
		public int ID
		{
			get { return id; }
			private set
			{
				if (!value.Equals(id))
					id = value;
			}
		}

		public void DoWork()
		{
			int count = 0;
			while (!shouldStop)
			{
				Console.WriteLine(string.Format("Queue Worker Thread {0}: Working ({1} passes)...", ID, count + 1));
				// Simulating long running process.
				Thread.Sleep(200);
				count++;
				if (count == 5)
					RequestStop();
			}
			Console.WriteLine(string.Format("Queue Worker Thread {0}: Terminating gracefully", ID));
			OnQueueWorkerCompleted(this, new QueueWorkerCompletedEventArgs(ID));
		}

		public void RequestStop()
		{
			shouldStop = true;
		}

		private QueueWorker() { ;}

		public QueueWorker(int ID)
		{
			id = ID;
		}
	}

	class Program
	{
		static AutoResetEvent reset = new AutoResetEvent(false);
		static List<QueueWorker> workers = new List<QueueWorker>();
		static bool creatingThreads = false;

		static void Main(string[] args)
		{
			creatingThreads = true;
			for (int i = 0; i < 5; i++)
				CreateAndStartThreads(i + 1);
			Console.WriteLine("Settings the reset event to block the main thread");
			reset.WaitOne();
			Console.WriteLine("Finished executing all queued worker threads.");
			Console.ReadLine();
		}

		public static void CreateAndStartThreads(int ID)
		{
			QueueWorker worker = new QueueWorker(ID);
			worker.QueueWorkerCompleted += OnQueueWorkerCompleted;
			ThreadPool.QueueUserWorkItem(state => worker.DoWork());
			Console.WriteLine(string.Format("Adding queue worker thread with ID - {0}", ID));
			workers.Add(worker);

			if (ID == 5)
				creatingThreads = false;
		}

		public static void OnQueueWorkerCompleted(object sender, QueueWorkerCompletedEventArgs e)
		{
			if (sender is QueueWorker)
			{
				Console.WriteLine(string.Format("Removing queue worker thread with ID - {0}", (sender as QueueWorker).ID));
				(sender as QueueWorker).QueueWorkerCompleted -= OnQueueWorkerCompleted;
				workers.Remove(sender as QueueWorker);
				if (!creatingThreads && workers.Count == 0)
					reset.Set();
			}
		}
	}

	public delegate void QueueWorkerCompletedEventHandler(object sender, QueueWorkerCompletedEventArgs e);

	public class QueueWorkerCompletedEventArgs : EventArgs
	{
		// Ideally you really want to use items that can identify the thread.
		public int ID { get; private set; }

		private QueueWorkerCompletedEventArgs()
		{
			ID = default(int);
		}

		public QueueWorkerCompletedEventArgs(int ID)
		{
			this.ID = ID;
		}
	}
}

Open in new window


-saige-
0
Will your db performance match your db growth?

In Percona’s white paper “Performance at Scale: Keeping Your Database on Its Toes,” we take a high-level approach to what you need to think about when planning for database scalability.

 

Author Comment

by:David_Duong
ID: 40381175
Hi Guys, I appreciate your quick and great comment. I should research on muti-thread programming instead of multiple processes. Kind of navie question title :)

Anyway, I actually see that .NET 4 has Task Parrallel Library (TPL). I produced some code to test and actually it shorten the copy time.
class Program
    {
        static void Main(string[] args)
        {
            string fromFolder = @"C:\Users\hduong\Downloads";
            string toSequentialFolder = @"C:\Encompass2Arg-Seq";
            string toParallelFolder = @"C:\Encompass2Arg-Pal";
            
            Stopwatch stopwatch = new Stopwatch();

            Console.WriteLine("Executing sequential copy...");
            stopwatch.Start();
            copyFileSequently(fromFolder, toSequentialFolder);
            stopwatch.Stop();
            Console.WriteLine("Sequential loop time in milliseconds: {0}", stopwatch.ElapsedMilliseconds);

            stopwatch.Reset();
            Console.WriteLine("Executing parallel copy...");
            stopwatch.Start();
            copyFileParallel(fromFolder, toParallelFolder);
            stopwatch.Stop();
            Console.WriteLine("Parallel loop time in milliseconds: {0}", stopwatch.ElapsedMilliseconds);



        }

        static void copyFileSequently(string fromFolder, string toFolder)
        {
            string[] files = Directory.GetFiles(fromFolder);
            foreach (string currentFile in files)
            {
                //Console.WriteLine(currentFile + "\n");
                File.Copy(currentFile, Path.Combine(toFolder, Path.GetFileName(currentFile)), true);
            }
        }

        static void copyFileParallel(string fromFolder, string toFolder)
        {
            string[] files = System.IO.Directory.GetFiles(fromFolder);
            Parallel.ForEach(files, currentFile =>
                {
                    File.Copy(currentFile, Path.Combine(toFolder, Path.GetFileName(currentFile)), true);
                }
                );
        }

Open in new window


Do you guys think that TPL would help in my multiple bat program by creating mutiple tasks?
0
 
LVL 34

Accepted Solution

by:
it_saige earned 300 total points
ID: 40381217
If you are using .NET 4, I would say definately.  

-saige-
0
 
LVL 40

Assisted Solution

by:Kyle Abrahams
Kyle Abrahams earned 200 total points
ID: 40382308
Given the application is used for file copying I would actually advise caution here.  This is one of those answers where it depends.

If you're copying multiple files from the same source and destination you could actually be creating more seeks on the hard drives as the OS tries to accomplish everything at once.  You'll of course have more power if they're all different sources and destinations (and when I say that I mean different hard drives, not directories or even partitions).  

Is the list of files deterministic in that it's executing pretty much the same files every day?  

If you have a few files for the same source / destination it might be better to group them by source / destination and then do those in a serial fashion.  

Just some considerations before a carte blanch yes can be given.
0
 

Author Comment

by:David_Duong
ID: 40382615
Kyle,

The list of files is pre-defined and pretty much the same everyday.

I already commit my code that use TPL.  
Thanks both of you,
0
 

Author Closing Comment

by:David_Duong
ID: 40382625
Really helpful comments with excellent code.
0

Featured Post

Enroll in July's Course of the Month

July's Course of the Month is now available! Enroll to learn HTML5 and prepare for certification. It's free for Premium Members, Team Accounts, and Qualified Experts.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

More often than not, we developers are confronted with a need: a need to make some kind of magic happen via code. Whether it is for a client, for the boss, or for our own personal projects, the need must be satisfied. Most of the time, the Framework…
The article shows the basic steps of integrating an HTML theme template into an ASP.NET MVC project
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
There are cases when e.g. an IT administrator wants to have full access and view into selected mailboxes on Exchange server, directly from his own email account in Outlook or Outlook Web Access. This proves useful when for example administrator want…

635 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question