GUI Invoke stalls thread

K

k.mellor

Hi,

I hope someone can help. I have written a simple form to demonstrate
my problem/question. The code follows.

The form starts a thread, which using delegates updates a label (Every
second adds another dot to the label). This works great. However,
when I put the GUI thread to sleep (Thread.Sleep), the thread seems to
stall. At first I was expecting dots to still appear, but obviously as
the GUI thread is asleep, they will not. However, the thread never
hits my break points once the GUI thread is asleep. Am I invoking
incorrectly? Any help appreciated.

Kev



using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;

namespace WindowsApplication2
{
public delegate void MyDelegate();

public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button button1;
private System.ComponentModel.Container components = null;

public Form1()
{
InitializeComponent();
}

private void UpdateLabel()
{
if (this.label1.InvokeRequired)
this.label1.Invoke(new MyDelegate(this.UpdateLabel));
else
this.label1.Text += ". ";
}

protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}


#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// label1
//
this.label1.Font = new System.Drawing.Font("Microsoft Sans
Serif", 15.75F, System.Drawing.FontStyle.Bold,
System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
this.label1.Location = new System.Drawing.Point(40, 32);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(232, 96);
this.label1.TabIndex = 0;
//
// button1
//
this.button1.Location = new System.Drawing.Point(80, 168);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(96, 32);
this.button1.TabIndex = 1;
this.button1.Text = "button1";
this.button1.Click += new
System.EventHandler(this.button1_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.button1);
this.Controls.Add(this.label1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void button1_Click(object sender, System.EventArgs e)
{
Worker w = new Worker();
w.m_delegate = new MyDelegate(this.UpdateLabel);
Thread t = new Thread(new ThreadStart(w.DoSomething));
t.Start();
Thread.Sleep(20000);
}


}

//
// work class
//
class Worker
{
public MyDelegate m_delegate = null;

public void DoSomething()
{
while (true)
{
Thread.Sleep(1000);
m_delegate();
}
}
}

} // end of namespace
 
W

Willy Denoyette [MVP]

Your "UpdateLabel" method has to run on the UI thread, but you put your UI
thread to sleep in your Click handler, that is, you block your UI threa, the
UpdateLabel method will only execute when the Click handler returns, that is
after 20 seconds.
Remove the Sleep call and you are done.

Willy.


| Hi,
|
| I hope someone can help. I have written a simple form to demonstrate
| my problem/question. The code follows.
|
| The form starts a thread, which using delegates updates a label (Every
| second adds another dot to the label). This works great. However,
| when I put the GUI thread to sleep (Thread.Sleep), the thread seems to
| stall. At first I was expecting dots to still appear, but obviously as
| the GUI thread is asleep, they will not. However, the thread never
| hits my break points once the GUI thread is asleep. Am I invoking
| incorrectly? Any help appreciated.
|
| Kev
|
|
|
| using System;
| using System.Drawing;
| using System.Collections;
| using System.ComponentModel;
| using System.Windows.Forms;
| using System.Data;
| using System.Threading;
|
| namespace WindowsApplication2
| {
| public delegate void MyDelegate();
|
| public class Form1 : System.Windows.Forms.Form
| {
| private System.Windows.Forms.Label label1;
| private System.Windows.Forms.Button button1;
| private System.ComponentModel.Container components = null;
|
| public Form1()
| {
| InitializeComponent();
| }
|
| private void UpdateLabel()
| {
| if (this.label1.InvokeRequired)
| this.label1.Invoke(new MyDelegate(this.UpdateLabel));
| else
| this.label1.Text += ". ";
| }
|
| protected override void Dispose( bool disposing )
| {
| if( disposing )
| {
| if (components != null)
| {
| components.Dispose();
| }
| }
| base.Dispose( disposing );
| }
|
|
| #region Windows Form Designer generated code
| /// <summary>
| /// Required method for Designer support - do not modify
| /// the contents of this method with the code editor.
| /// </summary>
| private void InitializeComponent()
| {
| this.label1 = new System.Windows.Forms.Label();
| this.button1 = new System.Windows.Forms.Button();
| this.SuspendLayout();
| //
| // label1
| //
| this.label1.Font = new System.Drawing.Font("Microsoft Sans
| Serif", 15.75F, System.Drawing.FontStyle.Bold,
| System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
| this.label1.Location = new System.Drawing.Point(40, 32);
| this.label1.Name = "label1";
| this.label1.Size = new System.Drawing.Size(232, 96);
| this.label1.TabIndex = 0;
| //
| // button1
| //
| this.button1.Location = new System.Drawing.Point(80, 168);
| this.button1.Name = "button1";
| this.button1.Size = new System.Drawing.Size(96, 32);
| this.button1.TabIndex = 1;
| this.button1.Text = "button1";
| this.button1.Click += new
| System.EventHandler(this.button1_Click);
| //
| // Form1
| //
| this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
| this.ClientSize = new System.Drawing.Size(292, 266);
| this.Controls.Add(this.button1);
| this.Controls.Add(this.label1);
| this.Name = "Form1";
| this.Text = "Form1";
| this.ResumeLayout(false);
|
| }
| #endregion
|
| /// <summary>
| /// The main entry point for the application.
| /// </summary>
| [STAThread]
| static void Main()
| {
| Application.Run(new Form1());
| }
|
| private void button1_Click(object sender, System.EventArgs e)
| {
| Worker w = new Worker();
| w.m_delegate = new MyDelegate(this.UpdateLabel);
| Thread t = new Thread(new ThreadStart(w.DoSomething));
| t.Start();
| Thread.Sleep(20000);
| }
|
|
| }
|
| //
| // work class
| //
| class Worker
| {
| public MyDelegate m_delegate = null;
|
| public void DoSomething()
| {
| while (true)
| {
| Thread.Sleep(1000);
| m_delegate();
| }
| }
| }
|
| } // end of namespace
|
 
K

k.mellor

Willy,

many thanks for your reply. I fully understand your answer. Perhpas
I am not stating the task clearly enough. I have deliberately put the
GUI thread to sleep to illustrate the problem I want to solve. Evn
though the GUI thread is alseep, I would still expect the worker thread
to continue executing.

So if the GUI thread thread sleeps for 20 seconds, and the worker
ticks over once a second, I would expect the worker to hit a breakpoint
(loop) 20 times whilst the GUI is asleep.

However, the worker thread seems to get blocked. I appreciate that
whilst the gui is sleeping the label will not get updated, but
shouldn't the 20 worker thread loops get queued? At the moment the
worker thread stops, which from my understaning defeats the object of
test InvokeRequired/Invoke().

Any thoughts would be helpful.

Thanks again.
Willy said:
Your "UpdateLabel" method has to run on the UI thread, but you put your UI
thread to sleep in your Click handler, that is, you block your UI threa, the
UpdateLabel method will only execute when the Click handler returns, that is
after 20 seconds.
Remove the Sleep call and you are done.

Willy.


| Hi,
|
| I hope someone can help. I have written a simple form to demonstrate
| my problem/question. The code follows.
|
| The form starts a thread, which using delegates updates a label (Every
| second adds another dot to the label). This works great. However,
| when I put the GUI thread to sleep (Thread.Sleep), the thread seems to
| stall. At first I was expecting dots to still appear, but obviously as
| the GUI thread is asleep, they will not. However, the thread never
| hits my break points once the GUI thread is asleep. Am I invoking
| incorrectly? Any help appreciated.
|
| Kev
|
|
|
| using System;
| using System.Drawing;
| using System.Collections;
| using System.ComponentModel;
| using System.Windows.Forms;
| using System.Data;
| using System.Threading;
|
| namespace WindowsApplication2
| {
| public delegate void MyDelegate();
|
| public class Form1 : System.Windows.Forms.Form
| {
| private System.Windows.Forms.Label label1;
| private System.Windows.Forms.Button button1;
| private System.ComponentModel.Container components = null;
|
| public Form1()
| {
| InitializeComponent();
| }
|
| private void UpdateLabel()
| {
| if (this.label1.InvokeRequired)
| this.label1.Invoke(new MyDelegate(this.UpdateLabel));
| else
| this.label1.Text += ". ";
| }
|
| protected override void Dispose( bool disposing )
| {
| if( disposing )
| {
| if (components != null)
| {
| components.Dispose();
| }
| }
| base.Dispose( disposing );
| }
|
|
| #region Windows Form Designer generated code
| /// <summary>
| /// Required method for Designer support - do not modify
| /// the contents of this method with the code editor.
| /// </summary>
| private void InitializeComponent()
| {
| this.label1 = new System.Windows.Forms.Label();
| this.button1 = new System.Windows.Forms.Button();
| this.SuspendLayout();
| //
| // label1
| //
| this.label1.Font = new System.Drawing.Font("Microsoft Sans
| Serif", 15.75F, System.Drawing.FontStyle.Bold,
| System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
| this.label1.Location = new System.Drawing.Point(40, 32);
| this.label1.Name = "label1";
| this.label1.Size = new System.Drawing.Size(232, 96);
| this.label1.TabIndex = 0;
| //
| // button1
| //
| this.button1.Location = new System.Drawing.Point(80, 168);
| this.button1.Name = "button1";
| this.button1.Size = new System.Drawing.Size(96, 32);
| this.button1.TabIndex = 1;
| this.button1.Text = "button1";
| this.button1.Click += new
| System.EventHandler(this.button1_Click);
| //
| // Form1
| //
| this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
| this.ClientSize = new System.Drawing.Size(292, 266);
| this.Controls.Add(this.button1);
| this.Controls.Add(this.label1);
| this.Name = "Form1";
| this.Text = "Form1";
| this.ResumeLayout(false);
|
| }
| #endregion
|
| /// <summary>
| /// The main entry point for the application.
| /// </summary>
| [STAThread]
| static void Main()
| {
| Application.Run(new Form1());
| }
|
| private void button1_Click(object sender, System.EventArgs e)
| {
| Worker w = new Worker();
| w.m_delegate = new MyDelegate(this.UpdateLabel);
| Thread t = new Thread(new ThreadStart(w.DoSomething));
| t.Start();
| Thread.Sleep(20000);
| }
|
|
| }
|
| //
| // work class
| //
| class Worker
| {
| public MyDelegate m_delegate = null;
|
| public void DoSomething()
| {
| while (true)
| {
| Thread.Sleep(1000);
| m_delegate();
| }
| }
| }
|
| } // end of namespace
|
 
W

Willy Denoyette [MVP]

| Willy,
|
| many thanks for your reply. I fully understand your answer. Perhpas
| I am not stating the task clearly enough. I have deliberately put the
| GUI thread to sleep to illustrate the problem I want to solve. Evn
| though the GUI thread is alseep, I would still expect the worker thread
| to continue executing.
|
| So if the GUI thread thread sleeps for 20 seconds, and the worker
| ticks over once a second, I would expect the worker to hit a breakpoint
| (loop) 20 times whilst the GUI is asleep.
|
| However, the worker thread seems to get blocked. I appreciate that
| whilst the gui is sleeping the label will not get updated, but
| shouldn't the 20 worker thread loops get queued?


No, because you call Invoke, which is synchronous, which means it wait's
until the posted message (and the associated delegate) has been handled.
You need to call the asynchronous version BeginInvoke instead.
Sorry if I wasn't entirely clear in my previous answer.

Willy.
 
K

k.mellor

I have posted a new code sample below, which is neaer my real world
problem. I have removed the Thread.Sleep call to remove any
complications - I am not certain how this affects my previous code.

The code below has an additional delegate which is called after 10s.
Every second the label is updated. After 10s a MessageBox is shown on
screen via InvokeRequired/Invoke().
The worker thread is programmed to run for 20s and write the numbers 1
- 20 to the file, closing the file every time.

However, whilst the message box is on screen the worker thread appears
to blocked. The numbers 11 - 20 are not written to the file.

Can anyone help me interpret this behaviour, and get the behaiour I
desire?

Thanks all

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using System.IO;

namespace WindowsApplication2
{
public delegate void MyDelegate();

/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button button1;
private System.ComponentModel.Container components = null;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, System.EventArgs e)
{

}

private void UpdateLabel()
{
if (this.label1.InvokeRequired)
this.label1.Invoke(new MyDelegate(this.UpdateLabel));
else
this.label1.Text += ". ";
}

private void ShowMessageBox()
{
if (this.InvokeRequired)
this.Invoke(new MyDelegate(this.ShowMessageBox));
else
MessageBox.Show("Hello");
}

protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}


#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// label1
//
this.label1.Font = new System.Drawing.Font("Microsoft Sans
Serif", 15.75F, System.Drawing.FontStyle.Bold,
System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
this.label1.Location = new System.Drawing.Point(40, 32);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(232, 96);
this.label1.TabIndex = 0;
//
// button1
//
this.button1.Location = new System.Drawing.Point(80, 168);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(96, 32);
this.button1.TabIndex = 1;
this.button1.Text = "button1";
this.button1.Click += new
System.EventHandler(this.button1_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.button1);
this.Controls.Add(this.label1);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void button1_Click(object sender, System.EventArgs e)
{
Worker w = new Worker();
w.m_delegate = new MyDelegate(this.UpdateLabel);
w.m_delegate2 = new MyDelegate(this.ShowMessageBox);
Thread t = new Thread(new ThreadStart(w.DoSomething));
t.Start();
}


}

//
// work class
//
class Worker
{
public MyDelegate m_delegate = null;
public MyDelegate m_delegate2 = null;

public void DoSomething()
{
TextWriter tw = new StreamWriter("C:\\red.txt");
tw.Close();
int i = 0;
while (true)
{
tw = new StreamWriter("C:\\red.txt", true);
i++;
Thread.Sleep(1000);
tw.WriteLine(i);
tw.Close();
//if (i < 10)
m_delegate();
else if (i == 10)
m_delegate2();
if (i == 20)
break;
}
tw.Close();
}
}

} // end of namespace
 
K

k.mellor

Willy,

thnks for your help. Just posted a second ago. You must have
answered whilst I was composing it. However, using my new sample and
BEGINinvoke worked perfectly.

I am about to read more on this, but if you have time and can explain
it briefly, why would anyone use InvokeRequired/Invoke() like I have if
Invoke is asynchronous - it blocks the threads stopping my
multithreaded intention.

Warmest Regards
 
K

k.mellor

Note I referred to Invoke as asynchronous - I meant synchronous. Must
be coffee time :D
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top