Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
?
Solved

Help with a "Basic" C# thread

Posted on 2005-04-01
7
Medium Priority
?
334 Views
Last Modified: 2008-01-09
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



0
Comment
Question by:ssteeves
7 Comments
 
LVL 10

Expert Comment

by:NetworkArchitek
ID: 13681162
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
 
LVL 1

Author Comment

by:ssteeves
ID: 13683146
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
 
LVL 11

Expert Comment

by:jatinderalagh
ID: 13688165
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
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 1

Accepted Solution

by:
basetew earned 1000 total points
ID: 13694705
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
 
LVL 1

Author Comment

by:ssteeves
ID: 13700122
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
 
LVL 1

Expert Comment

by:basetew
ID: 13702524
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
 
LVL 1

Expert Comment

by:basetew
ID: 13702577
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

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

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…
This article aims to explain the working of CircularLogArchiver. This tool was designed to solve the buildup of log file in cases where systems do not support circular logging or where circular logging is not enabled
This lesson discusses how to use a Mainform + Subforms in Microsoft Access to find and enter data for payments on orders. The sample data comes from a custom shop that builds and sells movable storage structures that are delivered to your property. …
Despite its rising prevalence in the business world, "the cloud" is still misunderstood. Some companies still believe common misconceptions about lack of security in cloud solutions and many misuses of cloud storage options still occur every day. …
Suggested Courses
Course of the Month13 days, 17 hours left to enroll

581 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