|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Newbie User Interface hangs in multi-thread app* Apologies for reposting this from .NET Academic * I am having problems updatingh the user interface in a multi-threaded application. BACKGROUND My App has a user interface (the starting class containing main) and a CPU class to handle many asynchronous comms routines. The user interface will be kept updated on the status of the comms by the CPU class. My user interface creates an instance of a class called CPU and passes itself in the constructor this way the CPU can update the user interface by calling a method of UI PROBLEM The user interface is becoming unresponsvie. The cursor turns to the hour glass and it doesnt repaint Although all the comms threads run happily in the background as soon as the CPU tries to call the UpdateScreen method the UI hangs. ASSISTANCE REQUIRED Could you please advise me on how i should go about resolving such a problem. I may well be using bad coding strtegy so pleas advise as you see fit on any aspect. Thanks, Shaun PSEUDO CODE // within the UI class private CPU theCPU; public UI() { theCPU = new CPU(this); } UpdateScreen(string status) { textBox1.Text = status; panel1.Visible= ! panel1.Visible; } // within the CPU class public CPU (UI userInterface) { this.userInterface = userInterface; // start threads to run async comms } private void ReceivedStatusChange() { userInterface.UpdateScreen("Some new status info"); } Firstly - are you calling the method directly or via (the form).Invoke(...)?
Secondly - as a design principle, I wouldn't recommend letting the CPU class know about the specific UI; I'd do this with events, i.e. have some events on the CPU class (StatusChanged, ProgressChanged, etc) which the UI subscribes to (for each instance of CPU), and then does something like public class CPU { public event ProgressChangedEventHandler ProgressChanged; protected void OnProgressChanged(int progressPercentage) { if (ProgressChanged != null) ProgressChanged(this, new ProgressChangedEventArgs(progressPercentage,null)); } } public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(); } public void InitCPU(CPU cpu) { // attach the event-handler cpu.ProgressChanged += CPU_ProgressChanged; } void CPU_ProgressChanged(object sender, ProgressChangedEventArgs e) { if (InvokeRequired) { // throw at the UI thread Invoke(new ProgressChangedEventHandler(CPU_ProgressChanged), sender, e); } else { // on the UI thread: process event int progress = e.ProgressPercentage; // do something with this } } } Does that help? Marc Thanks,
I'll apply it .. i wasnt using the Invoke which was probably the largest problem. Shaun Show quoteHide quote "Marc Gravell" wrote: > Firstly - are you calling the method directly or via (the form).Invoke(...)? > > Secondly - as a design principle, I wouldn't recommend letting the CPU class > know about the specific UI; I'd do this with events, i.e. have some events > on the CPU class (StatusChanged, ProgressChanged, etc) which the UI > subscribes to (for each instance of CPU), and then does something like > > public class CPU { > public event ProgressChangedEventHandler ProgressChanged; > protected void OnProgressChanged(int progressPercentage) { > if (ProgressChanged != null) > ProgressChanged(this, new > ProgressChangedEventArgs(progressPercentage,null)); > } > } > public partial class UserControl1 : UserControl { > public UserControl1() { > InitializeComponent(); > } > public void InitCPU(CPU cpu) { // attach the event-handler > cpu.ProgressChanged += CPU_ProgressChanged; > } > void CPU_ProgressChanged(object sender, ProgressChangedEventArgs e) > { > if (InvokeRequired) { // throw at the UI thread > Invoke(new ProgressChangedEventHandler(CPU_ProgressChanged), > sender, e); > } else { // on the UI thread: process event > int progress = e.ProgressPercentage; > // do something with this > } > } > } > > Does that help? > > Marc > > > Another point:
Invoke blocks until the form picks up the method (on it's message queue), invokes it and returns. If you have many threads you could see some blocking as your threads struggle to get UI runtime. You might want to consider using BeginInvoke instead, as this doesn't block. I must admit that I'm a little hazy as to whether you need to religiously call .EndInvoke with such methods; IIRC the 1.1 documentation hinted that this might cause a leak (or is that just delegates?), but it doesn't mention it on MSDN2 - in fact, the example uses .BeginInvoke without .EndInvoke. You might also want to mark the handler as [System.Runtime.Remoting.Messaging.OneWay] to help the compiler know it can skip some of the outbound stuff - again, not sure if this is necessary, but I've seen examples of such usage on this forum. Ref: http://msdn2.microsoft.com/en-us/library/a06c0dc2.aspx Marc
Other interesting topics
Tactics for Debugging Custom Components with CodeDom Serialized State
Auto Complete/Suggest (On Steroids) Project Data Source Using Derived Interface DoubleClick Event Won't Fire multiple forms/common menu structure 'DisplayMember' is not a member of 'System.Windows.Forms.ListView'. ComboBox and Tab character '\t' code clean up ComboBox: How can I limit user to list items? Generic windows error message |
|||||||||||||||||||||||