I have an application that I'm trying to write in C#. This is my first application using C#, so please bear with me.

I tried to create a small app with a textbox and two buttons: start and stop. The start button calls a function that iterates from 1 to 1000, and the stop button doesn't do anything. I've googled "threads" which looks to be what I want, but I don't know where to create the thread. I'm in the situation that I don't know enough about it to know what I'd even search for. (Man, that's frustrating enough.) I've got a bigger use for this function in another application that I'm doing for work, but I have to figure this part out before I can move on with my other project.

So, I understand what threads are. I don't understand if I put them in the form load event or if I start the new thread when the start button is clicked. I tried to start 2 threads on form load, one for start and one for stop, but you can't assign a button_click event to a thread, so then I tried to assign the stop button click event to call another function which was assigned to another thread. I still couldn't click the stop button. Any help would be greatly appreciated!!

private void btnStart_Click(object sender, EventArgs e)
        {
            Thread myThread = new Thread(new ThreadStart(Stop));
            myThread.Start();

            Count();

        }

        private void Count()
        {

            for (int i = 0; i < 1000; i++)
            {
                this.textBox1.Text += i + "\r\n";
                this.textBox1.Refresh();
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            Stop();
        }

        private void Stop()
        {
            Application.Exit();
        }

If you don't use threads then all the work you want to do when you press Start will run on the GUI thread, which means the GUI won't update (you can't press buttons, etc.) while the work is being done.

So you create a method that does all the work you need to do. You add a Thread variable to the form class (not the button click event). In the Start button click event, you create the thread and have it use the method you created as the code to run. In the Stop click event, you make sure you have a thread in the variable (from the form) and then you abort the thread.

Thank you for the reply! Do you have any code samples that I can look at?

Create a new windows application and add one label and two buttons. Leave the names as the default names. button1 is the start button (you can change the display name), button2 is the stop button. You will probably need to change the namespace line.

using System;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication2 {
    public partial class Form1 : Form {
        Thread t;
        public Form1() {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) {
            t = new Thread(new ThreadStart(DoWork));
            t.Start();
            button1.Enabled = false;
            button2.Enabled = true;
        }

        private void button2_Click(object sender, EventArgs e) {
            t.Abort();
            button1.Enabled = true;
            button2.Enabled = false;
        }

        private void DoWork() {
            try {
                for (int i = 0; i < 1000; i++) {
                    SetControlPropertyThreadSafe(label1, "Text", i.ToString());
                    Thread.Sleep(100);
                }
            } catch (ThreadAbortException e) {
                MessageBox.Show("OMG you stopped me early!", "OMG", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        private delegate void SetControlPropertyThreadSafeDelegate(Control control, string propertyName, object propertyValue);

        public static void SetControlPropertyThreadSafe(Control control, string propertyName, object propertyValue) {
            if (control.InvokeRequired) {
                control.Invoke(new SetControlPropertyThreadSafeDelegate(SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue });
            } else {
                control.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { propertyValue });
            }
        }
    }
}
Comments
Nice!

Wow...that got complicated real quick :-) I'll look through this, and now it seems I have to learn Invoke and delegates. It all seemed like such a simple task actually...

Thank you!

nutrion,

Threads are actually quite simple when you get the hang of it. Momerath's example is a great place to start.

One of the best resources I ever found on threads is available for free online, and has earned a permanent tab on my Chrome taskbar that never closes. You can read it here: http://www.albahari.com/threading/ It's lengthy, but well worth the read.

Basically threads can be thought of as "children" to a degree. They spawn from a point and die after they are done living. The thing with delegates is that if you have two children and both of them want the same piece of candy, they end up fighting over it. That's why locks are used, and delegates.

Here's a simple example I've used. The code spawns from the main GUI thread and changes an image in a picturebox when a certain outcome is achieved. The example shows 3 threads total (which is not necessary but done for example only) One is the main GUI thread, one is a thread that is spawned by the GUI thread which spawns two more threads and waits for them to complete before continuing:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Microsoft.Win32;
using System.ServiceProcess;
using System.Data.Sql;

namespace DATA.Wizards.FirstRun
{
    public partial class Step1 : Form
    {
        //initialize access to the Wizard Controller
        Start WizardController;

        public Step1(Start wizardController)
        {
            InitializeComponent();
            WizardController = wizardController;
        }

        // Region Map
        //
        // Globals
        // - Accessors
        // Event Handlers
        // Methods
        // - Threaded

        #region Globals

        static readonly object LockThis = new object();

        CountdownEvent ThreadSignaler = new CountdownEvent(1);

        private static DataTable _FoundServers;

        #region Accessors

        public DataTable FoundServers
        {
            get { return _FoundServers; }
            set { _FoundServers = value; }
        }

        #endregion

        #endregion

        #region Event Handlers

        private void Step1_Shown(object sender, EventArgs e)
        {
            Thread InspectEnvironmentThread = new Thread(InspectEnvironment);
            InspectEnvironmentThread.Name = "Inspect Environment Thread";
            InspectEnvironmentThread.Start();
        }

        #endregion

        #region Methods

        private void InspectEnvironment()
        {
            Action SignalDone;

            //initialize a thread to check for sql servers
            Thread FindSqlServersThread = new Thread(FindSqlServers);
            FindSqlServersThread.Name = "Find SQL Servers Thread";

            //start the threads
            FindSqlServersThread.Start();
            ThreadSignaler.Wait();

            //signal the WizardController that we are done
            SignalDone = () => WizardController.StepComplete(1);
            this.Invoke(SignalDone);
        }

        #region Threaded

        private void FindSqlServers()
        {
            Thread.Sleep(500);
            Action UXUpdate;

            //provide UX feedback
            UXUpdate = () => this.pictureBoxStep2.Image = Properties.Resources.reload16;
            this.Invoke(UXUpdate);

            try
            {
                //get the local machine's name
                string SystemName = Environment.MachineName;

                //see if there are instances of SQL servers running on the local machine
                //initialize columns in our data table to hold server information if any is found
                int LocalSqlServersFound = 0;
                DataTable LocalSqlServersInformation = new DataTable("LocalSqlServers");
                LocalSqlServersInformation.Columns.Add("ServerName");
                LocalSqlServersInformation.Columns.Add("InstanceName");
                LocalSqlServersInformation.Columns.Add("IsClustered");
                LocalSqlServersInformation.Columns.Add("Version");

                //load all the services running on the local machine
                ServiceController[] LocalServices = ServiceController.GetServices(SystemName);

                //search through the services and see if a SQL Server is running
                foreach (ServiceController thisService in LocalServices)
                {
                    DataRow LocalSqlServersInformationRow = LocalSqlServersInformation.NewRow();
                    if (thisService.ServiceName.Contains("MSSQL$"))
                    {
                        LocalSqlServersFound += 1;
                        LocalSqlServersInformationRow["ServerName"] = thisService.MachineName;
                        LocalSqlServersInformationRow["InstanceName"] = thisService.ServiceName.Replace("MSSQL$", "");
                        LocalSqlServersInformation.Rows.Add(LocalSqlServersInformationRow);
                    }
                }

                //scan the network for SQL Servers
                SqlDataSourceEnumerator SqlDataSources = SqlDataSourceEnumerator.Instance;
                DataTable Servers = SqlDataSources.GetDataSources();

                //merge our datatables
                Servers.Merge(LocalSqlServersInformation);
                Servers.AcceptChanges();

                //remove any rows from our datatable that have no instance name, we cant use them
                foreach (DataRow thisRow in Servers.Rows)
                {
                    if (thisRow["InstanceName"].ToString() == "" || thisRow["InstanceName"] == null)
                    {
                        thisRow.Delete();
                    }
                }

                //save any changes that were made to the server list
                Servers.AcceptChanges();

                //search finished successfully, return our list of found SQL servers
                lock (LockThis)
                {
                    FoundServers = Servers;
                }

                //provide UX feedback
                UXUpdate = () => this.pictureBoxStep2.Image = Properties.Resources.ok16;
                this.Invoke(UXUpdate);
            }
            catch
            {
                //the scan failed
                lock (LockThis)
                {
                    FoundServers = null;
                }

                //provide UX feedback
                UXUpdate = () => this.pictureBoxStep2.Image = Properties.Resources.messagebox_warning16;
                this.Invoke(UXUpdate);
            }
            finally
            {
                ThreadSignaler.Signal(1);
            }
        }

        #endregion

        #endregion
    }
}

I will show some other way of using Threads and using boolean variable to stop it!

Using thread.Abort() method is not a graceful way of stoping the thread. Not that its not correct (it does what its meant for), but there is a better any more appropriate way, as said, using boolean with Join() method.
Here`s a sample:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace Feb22Exercise1
{
    public partial class Form1 : Form
    {
        Thread thread;
        delegate void MyDelegate(string message);
        private volatile bool bShouldStop;

        public Form1()
        {
            InitializeComponent();
            button2.Enabled = false;
        }
       
        private void button1_Click(object sender, EventArgs e)
        {
            if (thread != null)
            {
                thread = null;
                bShouldStop = false;
            }
            thread = new Thread(new ThreadStart(DoWork));
            thread.Start();
            button1.Enabled = false;
            button2.Enabled = true;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //thread.Abort();
            RequestStop();
            Thread.Sleep(100);
            thread.Join();
            textBox1.Text = "";
            
            button1.Enabled = true;
            button2.Enabled = false;
        }

        private void DoWork()
        {
            for (int i = 0; i < 1000; i++)
            {
                if (!bShouldStop)
                {
                    UpdateTextBox(i.ToString());
                    Thread.Sleep(100);
                }
                else
                    break;

            }
        }
        public void RequestStop()
        {
            bShouldStop = true;
        }

        private void UpdateTextBox(string msg)
        {
            if (textBox1.InvokeRequired)
                this.textBox1.Invoke(new MyDelegate(UpdateTextBox), new object[] { msg });
            else
            {
                if (thread.IsAlive)
                    this.textBox1.Text = msg;
                else
                    this.textBox1.Text = "";
            }
        }
    }        
}

Mitja

This article has been dead for over six months. Start a new discussion instead.