?
Solved

Help with a "Basic" C# thread

Posted on 2005-04-01
7
Medium Priority
?
331 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
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

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

We all know that functional code is the leg that any good program stands on when it comes right down to it, however, if your program lacks a good user interface your product may not have the appeal needed to keep your customers happy. This issue can…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…
Suggested Courses
Course of the Month13 days, left to enroll

777 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