5

I have a problem with C# multi-threading.
Form contents are two buttons and two lables.

If I press on the first button, going looping from 1..to 60000, to update label1. ( It works)

If I press on the second button, going looping from 1..to 6000 to update label2,(and my form is lagged). (is not responding)
Please help!

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

        private void button1_Click(object sender, EventArgs e)
        {

            Thread thread = new Thread(new ThreadStart(countNumbers));
            thread.IsBackground = true;
            thread.Start();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Thread thread2 = new Thread(new ThreadStart(countNumbers2));
            thread2.Start();
        }

        public void countNumbers()
        {
            try
            {
                for (int i = 0; i < 60000; i++)
                {
                   this.Invoke((MethodInvoker)delegate()
                   {
                        label2.Text = "" + i.ToString();
                   }
                );
                }
            }
            catch (Exception e)
            {

            }
        }

        public void countNumbers2()
        {
            try
            {
                for (int i = 0; i < 60000; i++)
                {
                    this.Invoke((MethodInvoker)delegate()
                    {
                        label4.Text = "" + i.ToString();
                    }
                 );
                }
            }
            catch (Exception e)
            {

            }
        }



        private void label3_Click(object sender, EventArgs e)
        {

        }
    }
}
Jakob Möllås
  • 4,239
  • 3
  • 33
  • 61
Dezigo
  • 3,220
  • 3
  • 31
  • 39
  • 1
    did you try with alternates button clicks. First click on Button2, also see if you get any exception by throwing them or printing somewhere – AjayR May 30 '11 at 11:53
  • yes.. i did, but it's a similar problem – Dezigo May 30 '11 at 12:02
  • catch (Exception e) { } FYI: That is really bad code. If you get an exception, the code will mask it and it will fail silently. – Steve Wellens May 30 '11 at 12:04

5 Answers5

5

Try using a Forms.Timer in the form and poll a value at regular intervals to update the label in a controlled way. Updating the UI the way you do puts way to much load on the system.
A System.Windows.Forms.Timer runs on the GUI thread.

Just make sure to guard the shared resource in some way, this example uses a volatile member to handle thread synchronization.

You do not need the extra Thread.Sleep(10), it is just there to simulate some load.

private volatile int _counter;
private readonly Timer _timer = new System.Windows.Forms.Timer();

public Form1()
{
    InitializeComponent();

    _timer.Tick += TimerTick;
    _timer.Interval = 20;  // ~50 Hz/fps
    _timer.Start();
}

void TimerTick(object sender, EventArgs e)
{
    _label.Text = _counter.ToString();
}

private void Form1_Load(object sender, EventArgs e)
{
    Thread thread = new Thread(CountNumbers) {IsBackground = true};
    thread.Start(); 
}

public void CountNumbers()
{
    for (int i = 0; i < 60000; i++)
    {
        _counter++;
        Thread.Sleep(10);  // <-- Simulated work load
    }
}

Of course, you can easily expand this example to fit your example with two different counters, calculated on separate threads but still using only one Timer to update the entire UI.

Jakob Möllås
  • 4,239
  • 3
  • 33
  • 61
4

You end up with lagging because Invoke (switching to another thread) is very expensive operation and you are calling it too frequently

Stecya
  • 22,896
  • 10
  • 72
  • 102
  • It is not the matter. You can interact with gui elements only in the thread they where created. In your situation it is main thread. So when you start new thread and call there Invoke(), action is switched to main thread and is executed there. And Invoke() is expensive operation so you see lags – Stecya May 30 '11 at 12:01
  • What do you want to do? Why do you need update those labels very frequently from another thread? – Stecya May 30 '11 at 12:03
2

Try giving this.Refresh() or Application.DoEvents() in your loop

Umesh CHILAKA
  • 1,466
  • 14
  • 25
  • This approach _might_ work but generally you might experience strange results trying to fiddle with these constructs. See [this thread](http://stackoverflow.com/questions/5181777/c-application-doevents) for a more in-depth discusssion of the topic. – Jakob Möllås May 30 '11 at 12:23
  • I got a stack overflow trying these. – john k Feb 23 '14 at 04:47
1

Try to use lock statement

  lock (this)
      {
        label2.Text = "" + i.ToString();
       }

you shoud change your code to

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 WindowsFormsApplication23
{
    public partial class Form3 : Form
    {
        public Form3()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            Thread thread = new Thread(new ThreadStart(countNumbers));
            thread.IsBackground = true;
            thread.Start();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Thread thread2 = new Thread(new ThreadStart(countNumbers2));
            thread2.Start();
        }

        public void countNumbers()
        {
            try
            {
                for (int i = 0; i < 60000; i++)
                {
                   this.Invoke((MethodInvoker)delegate()
                   {
                       lock (this)
                       {
                           label2.Text = "" + i.ToString();
                       }
                   }
                );
                }
            }
            catch (Exception e)
            {

            }
        }

        public void countNumbers2()
        {
            try
            {
                for (int i = 0; i < 60000; i++)
                {
                    this.Invoke((MethodInvoker)delegate()
                    {
                        lock (this)
                        {
                            label4.Text = "" + i.ToString();
                        }
                    }
                 );
                }
            }
            catch (Exception e)
            {

            }
        }



        private void label3_Click(object sender, EventArgs e)
        {

        }
    }
}
DeveloperX
  • 4,633
  • 17
  • 22
0

Put some synchronization mechanism there

You can use

1.lock(this);

2.monitor.enter(obj); and monitor.exit(obj);

    lock (this){
      label2.Text = "" + i.ToString();
    }


    monitor.enter(obj);
      label2.Text = "" + i.ToString();
    monitor.exit(obj);
Lineesh K Mohan
  • 1,702
  • 14
  • 18