Help with a "Basic" C# thread

Hi,

I'm trying to write a very simple thread in C#.  I can get most of it working, but I can't get the thread to pass back information to the calling form as it runs.  I can't find a decent example of what I need.  If I could get a simple example, I could learn from it.  Here's what I'd like...very simple.

1 form.  It would contain 3 buttons ("cmdRun", "cmdPause", and "cmdResume") and 1 text box ("txtInfo").  
1 class containing a method "CreateFile" that would create and write 1,000,000 lines to a text file. (To ensure it takes a few seconds to run).

When the "cmdRun" button is clicked, it would run the "CreateFile" method in a thread.  As each line is written to the file (From the CreateFile Method), I would like to have the line number displayed in the text box on the main form. (So I would see it increment from 1 - 1,000,000.  This is the main part I'm stuck on).  

Also, at any time, I'd like to be able to click the "Pause" and resume buttons, and if possible, I'd like the location of the file to be created to be specified in the "cmdRun" button, not hard coded into my class.

I'll give a starting amount of 200 points for an example that does all of these things (Note:  I may have to ask a few questions until I'm sure I understand it).  Then, I may post a few follow up questions if I wish to "enhance" the project with a few extras, but I'll give separate points/questions for follow ups.

Good luck, and Thanks so much!  I've been stuck on this problem all week...

ssteeves



LVL 1
ssteevesAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

NetworkArchitekCommented:
You want a delegate that does a callback. Here's a good quick example, you want to do thread pooling:

http://www.codeproject.com/dotnet/multithread.asp
0
ssteevesAuthor Commented:
Thanks for the link, but I'd really prefer to have someone post the code for the above mentioned sample project.  I'll have an easier time to understand it in that context.  I wasn't able to make much sense of that code...

Increasing points to 250...
0
jatinderalaghCommented:
You need to use Control.Invoke method of Form to update the UI from Different thread.

Here is the example.

http://experts-exchange.com/Programming/Programming_Languages/C_Sharp/Q_21215229.html

Cheers
Jatinder
0
Introducing Cloud Class® training courses

Tech changes fast. You can learn faster. That’s why we’re bringing professional training courses to Experts Exchange. With a subscription, you can access all the Cloud Class® courses to expand your education, prep for certifications, and get top-notch instructions.

basetewCommented:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace WindowsApplication1
{
      /// <summary>
      /// Summary description for Form1.
      /// </summary>
      public class Form1 : System.Windows.Forms.Form
      {
            private System.Windows.Forms.Button cmdRun;
            private System.Windows.Forms.Button cmdPause;
            private System.Windows.Forms.Button cmdResume;
            private System.Windows.Forms.TextBox txtInfo;
            /// <summary>
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.Container components = null;

            public Form1()
            {
                  //
                  // Required for Windows Form Designer support
                  //
                  InitializeComponent();

                  workerThread = new System.Threading.Thread(new System.Threading.ThreadStart(threadRun));
            }

            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            protected override void Dispose( bool disposing )
            {
                  if( disposing )
                  {
                        if (components != null)
                        {
                              components.Dispose();
                        }
                  }
                  base.Dispose( disposing );
            }

            #region Windows Form Designer generated code
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                  this.cmdRun = new System.Windows.Forms.Button();
                  this.cmdPause = new System.Windows.Forms.Button();
                  this.cmdResume = new System.Windows.Forms.Button();
                  this.txtInfo = new System.Windows.Forms.TextBox();
                  this.SuspendLayout();
                  //
                  // cmdRun
                  //
                  this.cmdRun.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.cmdRun.Location = new System.Drawing.Point(32, 40);
                  this.cmdRun.Name = "cmdRun";
                  this.cmdRun.Size = new System.Drawing.Size(224, 23);
                  this.cmdRun.TabIndex = 0;
                  this.cmdRun.Text = "cmdRun";
                  this.cmdRun.Click += new System.EventHandler(this.cmdRun_Click);
                  //
                  // cmdPause
                  //
                  this.cmdPause.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.cmdPause.Location = new System.Drawing.Point(32, 88);
                  this.cmdPause.Name = "cmdPause";
                  this.cmdPause.Size = new System.Drawing.Size(224, 23);
                  this.cmdPause.TabIndex = 1;
                  this.cmdPause.Text = "cmdPause";
                  this.cmdPause.Click += new System.EventHandler(this.cmdPause_Click);
                  //
                  // cmdResume
                  //
                  this.cmdResume.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.cmdResume.Location = new System.Drawing.Point(32, 136);
                  this.cmdResume.Name = "cmdResume";
                  this.cmdResume.Size = new System.Drawing.Size(224, 23);
                  this.cmdResume.TabIndex = 2;
                  this.cmdResume.Text = "cmdResume";
                  this.cmdResume.Click += new System.EventHandler(this.cmdResume_Click);
                  //
                  // txtInfo
                  //
                  this.txtInfo.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.txtInfo.Location = new System.Drawing.Point(32, 184);
                  this.txtInfo.Name = "txtInfo";
                  this.txtInfo.Size = new System.Drawing.Size(224, 20);
                  this.txtInfo.TabIndex = 3;
                  this.txtInfo.Text = "";
                  //
                  // Form1
                  //
                  this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
                  this.ClientSize = new System.Drawing.Size(292, 262);
                  this.Controls.Add(this.txtInfo);
                  this.Controls.Add(this.cmdResume);
                  this.Controls.Add(this.cmdPause);
                  this.Controls.Add(this.cmdRun);
                  this.Name = "Form1";
                  this.Text = "Form1";
                  this.ResumeLayout(false);

            }
            #endregion

            /// <summary>
            /// The main entry point for the application.
            /// </summary>
            [STAThread]
            static void Main()
            {
                  Application.Run(new Form1());
            }

            private System.Collections.Queue workQueue = new Queue();
            private System.Threading.Thread workerThread;
            private bool signalled = false;

            private class threadJob
            {
                  public string jobCode;
                  public object jobData;
            }

            private void threadRun()
            {
                  //this function is life and soul of a thread

                  for (int c = 0; c < 100000; c++)
                  {
                        //do some hard work..
                        System.Threading.Thread.Sleep(10);

                        //check in a status report to gui thread
                        enqueueWork("status", c);
                  }
            }

            private void enqueueWork(string code, object data)
            {
                  //create a job
                  threadJob j = new threadJob();
                  j.jobCode = code;
                  j.jobData = data;

                  //enqueue job for work thread, safely
                  lock (this.workQueue.SyncRoot)
                  {
                        this.workQueue.Enqueue(j);
                  }

                  if (!signalled)
                  {
                        signalled = true;
                        //begininvoke sends&forgets a WM message
                        this.BeginInvoke(new System.Windows.Forms.MethodInvoker(processQueueOnGuiThread));
                  }
            }

            private void processQueueOnGuiThread()
            {
                  bool exit = false;
                  do
                  {

                        threadJob j = null;

                        //pop a job
                        lock (this.workQueue.SyncRoot)
                        {
                              if (this.workQueue.Count > 0)
                              {
                                    j = (threadJob)this.workQueue.Dequeue();
                              }
                              if (this.workQueue.Count > 1)
                              {
                                    System.Diagnostics.Debug.WriteLine("caching is effective!");
                              }
                        }
                        
                        //do the job
                        if (j != null)
                        {
                              if (j.jobCode == "status")
                              {
                                    executeStatusJob((int)j.jobData);
                              }
                        }
                        else
                        {
                              exit = true;
                        }

                  } while (exit == false); //more jobs?

                  //reset signal - this stops WM floods
                  signalled = false;
            }

            private void executeStatusJob(int newValue)
            {
                  this.txtInfo.Text = newValue.ToString();
            }

            private void cmdRun_Click(object sender, System.EventArgs e)
            {
                  //TODO: add production error handling
                  this.workerThread.Start();
            }

            private void cmdPause_Click(object sender, System.EventArgs e)
            {
                  //TODO: add production error handling
                  this.workerThread.Suspend();
            }

            private void cmdResume_Click(object sender, System.EventArgs e)
            {
                  //TODO: add production error handling
                  this.workerThread.Resume();
            }

      }
}
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ssteevesAuthor Commented:
basetew,

Thanks so much for taking the time to write that code for me.  I've looked at it briefly, and it looks impresive!  I should be able to learn lots from it.  My only concern is that your main method (threadRun) isn't located in a separate class.  I'm not sure how easy I'm going to find it to move that method into a class by myself, without an example.

I'm not sure if it matters where the class is located, but in my project I wish to right click on my project name in the solution explorer, and add a separate "Class File".  There, I plan to put the method that does the bulk of the work in my program.

Are you able to modify your above code to adapt to this slight change?

Thanks!!

ssteeves
0
basetewCommented:
This line:

workerThread = new System.Threading.Thread(new System.Threading.ThreadStart(threadRun));

Means the thread = function this.threadRun().

So you could change it to anything that is visible! Eg, myApp.DoLongTaskA();

Or.. just bung the call to the real hard work inside my threadRun..

Any code running on this thread wanting to do a Form call should pipe it through enqueueWork(string,object) instead. The processQueueOnGuiThread then picks this up, and does it (on GUI thread). During debugging I often put this code at top of functions to ensure I'm calling GUI threads ok. Remember in XP calling GUI from a thread works, but crashes 2000 randomly, and 98 very quickly.

Ask if you'd like a more advanced version of the enqueueWork, which accepts a delegate and object-array instead. That way you can call any function in your app, no matter what it's protection level (eg, private/public), you would call it like this: enqueueWork(new myBlahFunctionDelegate(myFunctionExecuter), new object[]{true}); //would execute the function myFunctionExecuter(true) on the GUI thread.

Please note calling a GUI thread is different to calling a non-GUI thread. As a non-GUI thread doesn't have a .Invoke or .BeginInvoke to signal it.
0
basetewCommented:
Sorry, my advice:

> This line:
> workerThread = new System.Threading.Thread(new System.Threading.ThreadStart(threadRun));
> Means the thread = function this.threadRun().
> So you could change it to anything that is visible! Eg, myApp.DoLongTaskA();
> Or.. just bung the call to the real hard work inside my threadRun..

is wrong. Your worker thread wants to communicate with gui thread. My advice was for a gui thread communicating with the worker thread. The rest of the advice is correct.

To move threadRun() into your own call, simply modify this line:

private void enqueueWork(string code, object data)

to this:

public void enqueueWork(string code, object data) //TODO: refactor 'enqueueWork' to 'EnqueueWork'

Then inside your real threadRun, just get the object for the Form instance, and call the enqueueWork to do a "gui thing"

Don't forget you can add extra jobCode's by extending my if() statement to check for other jobCode strings.

0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C#

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.