C# Multithread multiclass gui app, am I doing this right?
I created the following project to show you guys how I plan to do things. But my main project will be bigger and will have multiple classes. I'm just trying to get this to work properly so I know I'm using good practice when coding.
ok, lets begin :), so my form has a button called "开发者_如何学Pythonbutton1" and a text box called "textBox1" and I have a class called "Class1" which has a method "testtest()", I just put Thread.Sleep in the testtest method so I can find out its running on another thread.
Here is my form code
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;
namespace Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
delegate void Class1Deligate();
private void button1_Click(object sender, EventArgs e)
{
Class1 c = new Class1();
Class1Deligate testMethod = new Class1Deligate(c.testtest);
testMethod.BeginInvoke(endTestMethod, testMethod);
}
void endTestMethod(IAsyncResult ar)
{
}
}
}
and here is my Class one code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Test
{
class Class1
{
public void testtest()
{
Thread.Sleep(8888);
MessageBox.Show("Done");
}
}
}
Am I creating a new thread correctly? And can someone please tell me how to update textbox1 from the testtest method in class1 while its running? I made a earlier post and I was told to use Dispatcher, but for some reason it seems the Dispatcher class is not available for me.
I don't think you're actually starting a new thread. delegate.BeginInvoke
probably uses the internal ThreadPool to do it's bidding. And as a side note, I don't think you should call BeginInvoke
without the appropriate EndInvoke
call after it (otherwise the reference to the IAsyncResult
returned from the BeginInvoke
call won't be released).
If you want a new thread you could create a new one using the Thread class.
Thread t = new Thread(c.testtest);
t.IsBackground = true; // end if main thread ends
t.Start();
Note that if you use the Thread class like above, and using GUI, you need to attach a method to the GUI thread to update it. You can do that using the Control.Invoke call
public void updateGUI(object state) {
if (control.InvokeRequired) {
control.Invoke(new Action<object>(updateGUI), state);
return;
}
// if we are here, we can safely update the GUI
}
Firstly, there is the ThreadPool class available for threading if you like. It is what I generally use to fire off new threads e.g.,
ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i);
The link explains a bit more about them. As for the GUI, you will need to do a check to see whether the current thread is the same thread as the UI thread (as only the UI thread can update the UI), and if it isn't, invoke a function on the UI thread. This can be done like so,
delegate void ChangeFormUnsafeCallback(string text);
private void ChangeFormUnsafe(string text)
{
// Do stuff to form/controls
}
private void SomeFunction()
{
myForm.Invoke(new ChangeFormUnsafeCallback(ChangeFormUnsafe), "foo");
}
You can do a check to see if the invoke is required, however it's usually pretty straight forward to know whether the function will be used within the UI thread or not and Invoke appropriately. As a general rule of thumb, unless your function is being run as a result of something happening on the form (e.g., button being clicked) you will need to use the Invoke function.
Arya
Using your approach (async delegates), you are not starting a new thread, but you are using a thread from the .net threadpool, which will be released when your work is done. The correct way to invoke the delegate and be calledback when finished is as follows:
void button1_Click(object sender, EventArgs e)
{
Class1 c = new Class1();
Action action = c.Test;
action.BeginInvoke(new AsyncCallback(EndTestMethod), null);
}
void EndTestMethod(IAsyncResult token)
{
Action callBack = (Action)((AsyncResult)token).AsyncDelegate;
callBack.EndInvoke(token);
}
The asynchronous delegate approach is useful if your method is fairly short lived and you require a callback. Otherwise, for a longer running process you may want to create and start a new thread.
Thread t = new Thread(c.Test);
t.IsBackground = true;
t.Start();
However, signalling back to the calling thread, via a callback, will become more long-winded as you will have to write this code yourself.
Regarding the Dispatcher, this is a WPF class which queues actions to be handled by the UI thread in WPF applications (amongst other things). From your code, it looks like you are writing a Winforms application, so this type isn't appropriate.
Finally, for updating the text box, you need to check whether you are trying to access that control on the same thread that created the control (UI thread). This is is done via control.invokerequired and control.invoke. As Follows:
void AddText(string text)
{
if (textBox1.InvokeRequired)
{
textBox1.Invoke((Action<string>)AddText, text);
}
textBox1.Text = text;
}
Depending on what you're trying to accomplish, there are a couple different ways you could go. If you have a single task you want executed without blocking your UI thread, you can use the BackgroundWorker. This will give you a very easy way to keep your UI snappy and it makes updating the UI easy without requiring Invoke, etc.
If you are going to fire off a lot of tasks, you should probably use the ThreadPool. If you need to assign priority to the threads or they're going to spend a lot of time blocking then you should create your own instance Threads.
You can of course continue to do things the way you are which will make use of the ThreadPool but there are other approaches that might fit your particular needs better.
精彩评论