Solved

Create child process in C#

Posted on 2014-10-14
8
1,329 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
  • 3
  • 3
  • 2
8 Comments
 
LVL 39

Expert Comment

by:Kyle Abrahams
Comment Utility
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 32

Expert Comment

by:it_saige
Comment Utility
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 32

Expert Comment

by:it_saige
Comment Utility
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
 

Author Comment

by:David_Duong
Comment Utility
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
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 32

Accepted Solution

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

-saige-
0
 
LVL 39

Assisted Solution

by:Kyle Abrahams
Kyle Abrahams earned 200 total points
Comment Utility
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
Comment Utility
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
Comment Utility
Really helpful comments with excellent code.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Wouldn’t it be nice if you could test whether an element is contained in an array by using a Contains method just like the one available on List objects? Wouldn’t it be good if you could write code like this? (CODE) In .NET 3.5, this is possible…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
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.
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

728 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

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now