Solved

C#, problem queuing threads and synchronizing threads.

Posted on 2007-03-22
7
1,386 Views
Last Modified: 2013-12-17
1. I have a Windows Form
2. I have three processes that need to fire in a specific order, each process must complete before the new one can fire.  Phase1(), Phase2(), Phase3().
3. I have done research on ThreadPools, background threads, nothing seems to work.
4. Events are firing correctly and form controls are being updated correctly and safely using the Invoke method.
5. Key issue is with the ManualResetEvents class. A single button will call methods Phase1,Phase2, and Phase3.

6. When I fire phase 1, it will create a thread and call DiskPart() method, plus  phase 2 and phase 3 will also fire but the ManualResetEvents mre will be set to mre.WaitOne(); which I believe will cause the thread to wait.

7. In DiskPart() I call mre.Set() to reset and signal that I am done.

8. When CreateImage() now proceeds, it abrubtly exits at RecoveryX.WIM.WIMWrapper.WIMWrapper oWIM = new RecoveryX.WIM.WIMWrapper.WIMWrapper();

9. I am returned back to the main Windows form no further action occurs.

I am really stuck her fundamentally, I cannot have all three methods fire at once instead method 1 needs to fire, end and then method 2 will fire then end and so on.  I need to tell the threads in phase 2 and 3 to wait but every feasible approach I have tried fails and debugging is useless because it ends abrubtly with no error.

Note: some of my code is not shown, when I separate and call each phase on its own of course everything works, but I do want the use to click on three buttons, instead I want the user to click on one button and transparently fire off phase 1, phase 2, and phase 3.
Example code (some code removed):

    public partial class TestX: Form
    {
        private string _sImageFileName = null;
        private string _sImageFilePath = null;
        private int _sImageIndex = 0;
        private string _sImageName = null;
        private string _sImageDestinationPath = null;
        private string _sImageTempFolderPath = null;

        private string _sCommandScriptFile = null;
        private string _sCommandName = null;
        private string _sCommandArguments = null;

        private delegate void SetTextCallback(string dwMessage);
        private delegate void UpdateProgressBarDelegate(int dwPercent);

        public static ManualResetEvent mre = new ManualResetEvent(false);

        public TestX()
        {
            InitializeComponent();
        }
        public void Phase1()
        {
            this._sCommandScriptFile = Properties.Settings.Default.CommandScriptFile;
            this._sCommandName = Properties.Settings.Default.CommandName;
            this._sCommandArguments = Properties.Settings.Default.CommandArgs;

            //Instance a ThreadStart object and intance it with the SnapShot class CollectData method.
            ThreadStart _threadStart = new ThreadStart(DiskPart);
            //Initialize a Thread object and set to null;
            Thread _thread = null;

            try
            {
                //Instance Thread object with ThreadStart object.
                _thread = new Thread(_threadStart);
                //Name the Thread
                _thread.Name = "Phase1";
                //Spawn the Thread
                _thread.Start();
            }
            catch (Exception exp)
            {
                throw new Exception("Image Apply error: " + exp.Message);
            }
        }
        public void Phase2()
        {
            this._sImageFileName = Properties.Settings.Default.DebugImageFileName;
            this._sImageIndex = Properties.Settings.Default.DebugImageIndex;
            this._sImageTempFolderPath = Properties.Settings.Default.DebugImageTempFolder;
            this._sImageDestinationPath = Properties.Settings.Default.DebugImageDestination;

            //Instance a ThreadStart object and intance it with the SnapShot class CollectData method.
            ThreadStart _threadStart = new ThreadStart(CreateImage);
            //Initialize a Thread object and set to null;
            Thread _thread = null;

            try
            {
                //Instance Thread object with ThreadStart object.
                _thread = new Thread(_threadStart);
                //Name the Thread
                _thread.Name = "Phase2";
                //Spawn the Thread
                _thread.Start();
            }
            catch (Exception exp)
            {
                throw new Exception("Image Apply error: " + exp.Message);
            }
        }
        public void Phase3()
        {
            this._sImageFileName = Properties.Settings.Default.DebugImageFileName;
            this._sImageIndex = Properties.Settings.Default.DebugImageIndex;
            this._sImageTempFolderPath = Properties.Settings.Default.DebugImageTempFolder;
            this._sImageDestinationPath = Properties.Settings.Default.DebugImageDestination;

            //Instance a ThreadStart object and intance it with the SnapShot class CollectData method.
            ThreadStart _threadStart = new ThreadStart(CreateImage);
            //Initialize a Thread object and set to null;
            Thread _thread = null;

            try
            {
                //Instance Thread object with ThreadStart object.
                _thread = new Thread(_threadStart);
                //Name the Thread
                _thread.Name = "Phase3";
                //Spawn the Thread
                _thread.Start();
            }
            catch (Exception exp)
            {
                throw new Exception("Image Apply error: " + exp.Message);
            }
        }
        private void CreateImage()
        {
            mre.WaitOne();
            RecoveryX.WIM.WIMWrapper.WIMWrapper oWIM = new RecoveryX.WIM.WIMWrapper.WIMWrapper();
            oWIM.Progress += new RecoveryX.WIM.WIMWrapper.WIMWrapper.ProgressEventHandler(OnImageXProgress);
            oWIM.ApplyImage(this._sImageFileName, this._sImageDestinationPath, this._sImageTempFolderPath, this._sImageIndex);
            mre.Set();

        }
        private void DiskPart()
        {
            RecoveryX.CommandLine.CommandAPI.CommandLineProcess m_cmd = new RecoveryX.CommandLine.CommandAPI.CommandLineProcess();
            m_cmd.Exited += new RecoveryX.CommandLine.CommandAPI.CommandLineProcess.ExitedEventHandler(OnCmdExited);
            m_cmd.Started += new RecoveryX.CommandLine.CommandAPI.CommandLineProcess.StartedEventHandler(OnCmdStarted);

            //Check to see if file exists
            if (!FileIO.FileExists(this._sCommandScriptFile))
                throw new Exception("File does not exist");

            m_cmd.Command = this._sCommandName;
            m_cmd.Arguments = this._sCommandArguments;
            m_cmd.Start();
            m_cmd.Command = null;    // clear command line after execution
            while (m_cmd.IsRunning)
                Thread.Sleep(1000);

            mre.Set();
        }
0
Comment
Question by:yami_rider
  • 3
  • 2
  • 2
7 Comments
 
LVL 9

Expert Comment

by:bele04
ID: 18777872
I don't see you calling mre.Reset() in your code.  You should call the Reset() method after your call to mre.WaitOne() since you need to manually reset the mre object to a non-signalling state.  If you're using the AutoResetEvent though then there's no need to call the Reset() method since it will automatically reset the state of the object when you call the Set() method.

Also, since threads 2 and 3 are calling the same method I suggest that you adjust the code a little when calling Phase 3.  Something like this:

public void Phase3()
        {
            this._sImageFileName = Properties.Settings.Default.DebugImageFileName;
            this._sImageIndex = Properties.Settings.Default.DebugImageIndex;
            this._sImageTempFolderPath = Properties.Settings.Default.DebugImageTempFolder;
            this._sImageDestinationPath = Properties.Settings.Default.DebugImageDestination;

            //Instance a ThreadStart object and intance it with the SnapShot class CollectData method.
            ThreadStart _threadStart = new ThreadStart(CreateImage);
            //Initialize a Thread object and set to null;
            Thread _thread = null;

            try
            {
                //Instance Thread object with ThreadStart object.
                _thread = new Thread(_threadStart);
                //Name the Thread
                _thread.Name = "Phase3";
                //Spawn the Thread
                mre.WaitOne();                   <-- added code
                _thread.Start();
            }
            catch (Exception exp)
            {
                throw new Exception("Image Apply error: " + exp.Message);
            }
        }

And after all 3 threads are done call mre.Reset() again to set it back to its initial state.
0
 

Author Comment

by:yami_rider
ID: 18778099
I did a little bit more research on the AutoResetEvent and from what I can see it makes more sense to implement that for the reason that it will automatically perform the Reset for you.

Thank you for your help.  Taking your information, I rewrote a few methods and with a little bit more research I coded this:

private void CreateWorkerThreads()
        {
            m_are = new AutoResetEvent(false);

            this._sCommandScriptFile = Properties.Settings.Default.CommandScriptFile;
            this._sCommandName = Properties.Settings.Default.CommandName;
            this._sCommandArguments = Properties.Settings.Default.CommandArgs;

           
            //Instance a ThreadStart object and intance it with the SnapShot class CollectData method.
            ThreadStart _threadStartA = new ThreadStart(DiskPart);
            //Initialize a Thread object and set to null;
            Thread _threadA = null;

            //Instance a ThreadStart object and intance it with the SnapShot class CollectData method.
            //ThreadStart _threadStartB = new ThreadStart(this.CreateImage);
            ParameterizedThreadStart _threadStartB = new ParameterizedThreadStart(this.CreateImage);
            //Initialize a Thread object and set to null;
            Thread _threadB = null;

            //Instance a ThreadStart object and intance it with the SnapShot class CollectData method.
            //ThreadStart _threadStartB = new ThreadStart(this.CreateImage);
            ParameterizedThreadStart _threadStartC = new ParameterizedThreadStart(this.CreateImage);
            //Initialize a Thread object and set to null;
            Thread _threadC = null;

            try
            {
                //Instance Thread object with ThreadStart object.
                _threadA = new Thread(_threadStartA);
                //Instance Thread object with ThreadStart object.
                _threadB = new Thread(_threadStartB);
                //Instance Thread object with ThreadStart object.
                _threadC = new Thread(_threadStartC);
                //Name the Thread
                _threadA.Name = "Phase1";
                //Name the Thread
                _threadB.Name = "Phase2";
                //Name the Thread
                _threadC.Name = "Phase3";
                //Spawn the Thread
                _threadA.Start();
                //Spawn the Thread
                _threadB.Start(new ImageX("DEBUG"));
                //Spawn the Thread
                _threadC.Start(new ImageX("DEBUG"));
                m_are.Set();
                m_are.Set();
                m_are.Set();
            }
            catch (Exception exp)
            {
                MessageBox.Show("Image Apply error: " + exp.Message);
            }
        }
        private void CreateImage(object oObj)
        {
            m_are.WaitOne();
            ImageX oImage = (ImageX)oObj;
           
            RecoveryX.WIM.WIMWrapper.WIMWrapper oWIM = new RecoveryX.WIM.WIMWrapper.WIMWrapper();
            oWIM.Progress += new RecoveryX.WIM.WIMWrapper.WIMWrapper.ProgressEventHandler(OnImageXProgress);
            oWIM.Complete += new RecoveryX.WIM.WIMWrapper.WIMWrapper.CompleteEventHandler(OnImageXComplete);
            oWIM.ApplyImage(oImage._sImageFileName, oImage._sImageDestinationPath, oImage._sImageTempFolderPath, oImage._sImageIndex);
        }
        private void DiskPart()
        {
            m_are.WaitOne();
            RecoveryX.CommandLine.CommandAPI.CommandLineProcess m_cmd = new RecoveryX.CommandLine.CommandAPI.CommandLineProcess();
            m_cmd.Exited += new RecoveryX.CommandLine.CommandAPI.CommandLineProcess.ExitedEventHandler(OnCmdExited);
            m_cmd.Started += new RecoveryX.CommandLine.CommandAPI.CommandLineProcess.StartedEventHandler(OnCmdStarted);

            //Check to see if file exists
            if (!FileIO.FileExists(this._sCommandScriptFile))
                throw new Exception("File does not exist");

            m_cmd.Command = this._sCommandName;
            m_cmd.Arguments = this._sCommandArguments;
            m_cmd.Start();
            m_cmd.Command = null;    // clear command line after execution
        }

Everything works as far as I can see in that, ThreadA will fire, ThreadB and ThreadC will wait. ThreadA ends, ThreadB proceeds while ThreadC waits, once ThreadB is complete finally ThreadC will fire and complete.

As a side question, between the above method versus using a ThreadPool or Background worker thread, there is so much information that I am absorbing and for the most part all do practically the same thing, am I right to assume this?  Or are there really fundamental differences to using a ThreadPool versus a Background worker thread or my method above?  Basically for a clean implementation what would you suggest I use?

Thank you again.
0
 
LVL 12

Accepted Solution

by:
andrewjb earned 250 total points
ID: 18778274
I've only scan-read through your post, so might have missed something, but why not create a single thread to do all the work, and just call the Phase1(); Phase2(); Phase3() functions in turn (without creating new threads within them)?

There's no point in creating 3 separate threads to do work then making them wait for each other to finish, is there?

0
DevOps Toolchain Recommendations

Read this Gartner Research Note and discover how your IT organization can automate and optimize DevOps processes using a toolchain architecture.

 
LVL 9

Assisted Solution

by:bele04
bele04 earned 250 total points
ID: 18778402
I would suggest that you put the m_are.Set() calls at the end of the methods being called by the threads (CreateImage, DiskPart).  Also, I would agress with andrewjb that you could execute all of it under just one thread since (from the looks of it) you need them to execute one after the other anyway.  It's a waste of thread resources.

ThreadPool and BackgroundWorker are two different things.  The threadpool is basically a pool of threads managed by the system and all threads in the pool are background threads.  Meaning they all stop when all foreground threads are done (as is with all background threads).  Since the threadpool is system managed, thread operations are more efficient than you having to manage the threads manually.

BackgroundWorker is basically a class that gives you more control on operating background threads.
0
 
LVL 12

Expert Comment

by:andrewjb
ID: 18778415
Again, I haven't checked your source in detail, but...

If you've three things to do, you'll need two events, not just one... First to block Process2 until Process1 finished; the second to block process3 until process2 releases it.

With a single event, when you trigger it, there's no way to know whether the thread for process2 or that for process3 will happen to be activated when Process1 completes.

0
 

Author Comment

by:yami_rider
ID: 18783427
Thank you guys for all of your help it was invaluable.  Can I ask you andrew in a little bit more detail. If I did have multiple threads started:

ThreadA
ThreadB
ThreadC

Would I set it the way I did in my code example above?  Or would I fire the threads from the event methods that get fired?

OnComplete()
{
     .... fire next thread
}

I am using events such as OnStarted, OnProgress, OnComplete, OnError etc etc would I signal my threads from within these methods?  I am trying to understand more clearly what you are saying.

If ThreadA fails, what is the best method to halt all execution and return back to the Windows forms?

Thank you again for all of your help it is very much appreciated.
0
 
LVL 12

Expert Comment

by:andrewjb
ID: 18791334
Does it matter whether the phases get done in the order

Phase1 Phase2 Phase 3
or
Phase1 Phase3 Phase2

At the moment, with a single event to wait on, there's no way of knowing which of Phase2 or Phase3 will run first - they are both waiting for phase1 to finish via the mre.WaitOne(); and there's no way to know which thread will get triggered when Phase1 completes.

If that's acceptable, then that's fine. Otherwise you'd need two events - let's call then
phase1ended and Phase2ended.
Then Phase2 waits on Phase1ended, and calls phase2ended.set when it has completed etc.


If you need Phase1->Phase2->Phase3 then you'd also have to tweak the functions so Phase2 and Phase3 start running a wrapper function that waits on the right event, then calls the CreateImage method..


Alternatively, I'm not sure if you want Phase2 and Phase3 to run together, after phase1 has finished... ?



If ThreadA fails, then best way to stop execution is simply only have 1 thread..... and just don't start phases 2 and 3
0

Featured Post

Master Your Team's Linux and Cloud Stack

Come see why top tech companies like Mailchimp and Media Temple use Linux Academy to build their employee training programs.

Question has a verified solution.

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

Suggested Solutions

For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
The article shows the basic steps of integrating an HTML theme template into an ASP.NET MVC project
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …
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…

786 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