Solved

C#, problem queuing threads and synchronizing threads.

Posted on 2007-03-22
7
1,383 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
3 Use Cases for Connected Systems

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, testing some more, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us.

 
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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
Hi friends,  in this video  I'll show you how new windows 10 user can learn the using of windows 10. Thank you.
Learn how to create flexible layouts using relative units in CSS.  New relative units added in CSS3 include vw(viewports width), vh(viewports height), vmin(minimum of viewports height and width), and vmax (maximum of viewports height and width).

867 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

16 Experts available now in Live!

Get 1:1 Help Now