Solved

C#, problem queuing threads and synchronizing threads.

Posted on 2007-03-22
7
1,381 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
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
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

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Suggested Solutions

This article describes relatively difficult and non-obvious issues that are likely to arise when creating COM class in Visual Studio and deploying it by professional MSI-authoring tools. It is assumed that the reader is already familiar with the cla…
This article is for Object-Oriented Programming (OOP) beginners. An Interface contains declarations of events, indexers, methods and/or properties. Any class which implements the Interface should provide the concrete implementation for each Inter…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

707 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

18 Experts available now in Live!

Get 1:1 Help Now