Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Threading Basics

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 testThreads
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        public void countToLots()
        {
            for (int i = 0; i < 10000000; i++)
            {
                textBox1.Text = "Counting to 10000000, value is " + i + Environment.NewLine;
            }
        }

        public void countToZero()
        {
            for (int i = 10000000; i > 0; i--)
            {
                textBox2.Text = "Counting to 0, value is " + i + Environment.NewLine;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread countUp = new Thread(new ThreadStart(countToLots));
            Thread countDown = new Thread(new ThreadStart(countToZero));
            countUp.Start();
            countDown.Start();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            textBox3.Text = "Bobby bob bob " + Environment.NewLine;
        }
    }
}

I really need to try and get the hang of this - i just dont understand the theory behind why i get an error message. Could someone help me out please?

Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.

like image 564
tom Avatar asked Feb 25 '23 03:02

tom


2 Answers

UI controls have "thread affinity"; they do not want to be touched by anything except the UI thread; that includes reading and writing properties. The assignment to .Text should be done from the UI thread, either by using Invoke, or BackgroundWorker.

For example:

public void countToLots()
{
    for (int i = 0; i < 10000000; i++)
    {
        // running on bg thread
        textBox1.Invoke((MethodInvoker) delegate {
            // running on UI thread
            textBox1.Text = "Counting to 10000000, value is "
                      + i + Environment.NewLine;
        });
        // running on bg thread again
    }
}

But note that this type of thread switching has overhead. You should not call back every iteration - you should (for example) update the UI every [n] iterations - in the above, every 10000 for example.

like image 50
Marc Gravell Avatar answered Mar 04 '23 08:03

Marc Gravell


You cannot use a method or property of a Form control from a different thread than the thread that created (called new) the control.

To do that just do:

    public void countToLots()
    {
        for (int i = 0; i < 10000000; i++)
        {
            SetText("Counting to 10000000, value is " + i + Environment.NewLine);
        }
    }

    public void SetText(string text)
    {

        if (this.textBox1.InvokeRequired())
        {
            Action<string> auxDelegate = SetText;
            this.BeginInvoke(auxDelegate,text);
        }
        else
        {
            this.textBox1.Text = text;
        }
    }

What the method is doing with the beginInvoke is just calling again the SetText method from the thread that created the control.

like image 28
Ignacio Soler Garcia Avatar answered Mar 04 '23 09:03

Ignacio Soler Garcia