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
Solved

Create child process in C#

Posted on 2014-10-14
8
1,570 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 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 33

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 33

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
How Do You Stack Up Against Your Peers?

With today’s modern enterprise so dependent on digital infrastructures, the impact of major incidents has increased dramatically. Grab the report now to gain insight into how your organization ranks against your peers and learn best-in-class strategies to resolve incidents.

 

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 33

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

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Introduction Hi all and welcome to my first article on Experts Exchange. A while ago, someone asked me if i could do some tutorials on object oriented programming. I decided to do them on C#. Now you may ask me, why's that? Well, one of the re…
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
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.
A short tutorial showing how to set up an email signature in Outlook on the Web (previously known as OWA). For free email signatures designs, visit https://www.mail-signatures.com/articles/signature-templates/?sts=6651 If you want to manage em…

828 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