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)
{
}
}
}
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.
You end up with lagging because Invoke (switching to another thread) is very expensive operation and you are calling it too frequently
Try giving this.Refresh()
or Application.DoEvents()
in your loop
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)
{
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With