Reputation: 234
I have a form in C#, said form only has a multiline text box.
In the load method of this form I need to create 3 worker threads. These threads will call the Counter class where they will execute a method called count.
This method is just a counter that will repeat the task 10000 times and its only task is to call a function in the Form main class which should print a message in the text box.
The message must be the number of the thread that executes the task and the number of the counter.
It is supposed that with the invoke method this would be done in the main thread leaving the other threads working in the background, but what happens is that my form stays frozen and I don't understand why.
I leave you my code, I hope you can help me.
Form Main Class
namespace TestMultiHiloCSharp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Contador contadorClassOne = new(1, this);
Contador contadorClassTwo = new(2, this);
// Crea y arranca 2 hilos
Task t1 = Task.Run(() => {
contadorClassOne.contar();
});
Task t2 = Task.Run(() => {
contadorClassTwo.contar();
});
}
public void showMessage(String Mensaje)
{
this.Invoke(()=>
{
txtMultiLineTextBox.Text = Mensaje + "\r\n" + txtMultiLineTextBox.Text;
});
}
}
}
Counter Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestMultiHiloCSharp
{
internal class Contador
{
int numHilo;
Form1 thisForm;
public Contador(int numHilo,Form1 thisForm)
{
this.numHilo = numHilo;
this.thisForm = thisForm;
}
public void contar(){
int cont = 0;
while (cont < 100)
{
thisForm.showMessage("this is the theread: " + this.numHilo + " and the number: " + cont);
cont++;
}
}
}
}
Upvotes: 0
Views: 371
Reputation: 117029
The reason your code is running slow is this line:
txtMultiLineTextBox.Text = Mensaje + "\r\n" + txtMultiLineTextBox.Text;
When you set the constant in your while loop to any sizeable number - I found 1000 or more to be sizeable - then there there is a lot of work to build the string and then for the TextBox
to display that string.
It will run fast if you just write this:
txtMultiLineTextBox.Text = Mensaje;
Your problem is the massive string.
If I were doing this kind of code, and if I were trying not to change your Contador
class too much, then I might try this implementation:
internal class Contador
{
int numHilo;
Action<string> callback;
public Contador(int numHilo, Action<string> callback)
{
this.numHilo = numHilo;
this.callback = callback;
}
public void Contar()
{
int cont = 0;
while (cont < 1000)
{
callback("this is the thread: " + this.numHilo + " and the number: " + cont);
cont++;
}
}
}
Note that instead of passing the Form1
to the Contador
class, I'm passing a Action<string>
instead.
Now to consume this in the form I would want to introduce a delay that only updates if a period of time has passed and no further updates have come in.
Here's how I might define the callback
:
object gate = new();
List<string> callbacks = new();
Action<string> callback = async x =>
{
int count = -1;
lock (gate)
{
callbacks.Add(x);
count = callbacks.Count;
}
await Task.Delay(TimeSpan.FromSeconds(0.5));
lock (gate)
{
if (callbacks.Count == count)
{
this.Invoke((Action)(() => txtMultiLineTextBox.Text = String.Join(Environment.NewLine, callbacks.AsEnumerable().Reverse().Append(txtMultiLineTextBox.Text))));
callbacks = new();
}
}
};
Now your remaining code is more-or-less untouched.
Contador contadorClassOne = new(1, callback);
Contador contadorClassTwo = new(2, callback);
Task t1 = Task.Run(() =>
{
contadorClassOne.Contar();
});
Task t2 = Task.Run(() =>
{
contadorClassTwo.Contar();
});
That works with large numbers without the freezing of the form.
Upvotes: 2