M
minimega
Hello to all NG.
I'm developing a .NET application with CF 1.0 on a x86 WinCE 4.2
device. The application must do some long elaborations, so I want to
display a foreground form, with an animation (a clock, a running car, a
moving butterfly..) that runs until the elaborations finished, so user
can have the idea that when somthing is moving, the PC is working...
I've just implemented the "WaitingForm" with a timer that displays
images sequence that do the animation (20 bitmaps images). That's works
fine. However, if I create the instance and then .Show() the instance
in the main thread, when I start the elaboration the animation become
"in jerks" and stops when I make some syncronous PInvoke calls to
external dll or when I access data etc..
So I've moved the instancing of the WaitingForm in a second thread, so
the WaitingForm has its own MessageQueue and is independent from the
main thread. Thats works fine and the animation is very fluid and
without locks (except when I move the mousepointer over the bitmpa
area.. WHY?!?!).
However, when I show the WaitingForm, it is displayed as a new window,
also in the taskbar, so, if the user switchs to other windows or to the
main application window (wich is working), the WaitingForm disappers
under the new activated window, and the animation is no more visible.
So, I can try to use the OnTop property (does it exists in WinCE?) but
what I'm trying to do is to "incorporate" the WaitingForm in the main
application window, like if it were a standard form control, placed on
the surface of the main application window (a sort of MDI-MDIChild
application). I now I can do it setting the WaitingForm .Parent
property to the main application window reference (this).
However this way raieses an exception, and I'm investigating about
this. Please, start a new VS2003 Smart Device C# project, add Form1 and
Form2 forms and cut&past the following code:
************ Form1.cs ***************
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.Threading;
namespace FormsAndThreads
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.Button button5;
private System.Windows.Forms.Button button3;
private Form2 frmThread = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
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.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
this.button5 = new System.Windows.Forms.Button();
//
// button1
//
this.button1.Location = new System.Drawing.Point(20, 16);
this.button1.Size = new System.Drawing.Size(112, 60);
this.button1.Text = "Normal";
this.button1.Click += new
System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(20, 88);
this.button2.Size = new System.Drawing.Size(112, 60);
this.button2.Text = "Parent";
this.button2.Click += new
System.EventHandler(this.button2_Click);
//
// button3
//
this.button3.Location = new System.Drawing.Point(20, 160);
this.button3.Size = new System.Drawing.Size(112, 60);
this.button3.Text = "Thread";
this.button3.Click += new
System.EventHandler(this.button3_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(20, 232);
this.button4.Size = new System.Drawing.Size(112, 60);
this.button4.Text = "Parent Thread";
this.button4.Click += new
System.EventHandler(this.button4_Click);
//
// button5
//
this.button5.Location = new System.Drawing.Point(20, 308);
this.button5.Size = new System.Drawing.Size(112, 60);
this.button5.Text = "Parent Thread 2";
this.button5.Click += new
System.EventHandler(this.button5_Click);
//
// Form1
//
this.Controls.Add(this.button5);
this.Controls.Add(this.button4);
this.Controls.Add(this.button3);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Load(object sender, System.EventArgs e)
{
}
private void button1_Click(object sender, System.EventArgs e)
{
// Create a new Form2 instance
Form2 frmForm2 = new Form2();
frmForm2.Show();
// Wait 2 seconds on the main thread
Thread.Sleep(2000);
}
private void button2_Click(object sender, System.EventArgs e)
{
// Create a new Form2 instance and "incorporate" it in Form1
(looks like MDI-MDI child applications)
Form2 frmForm2 = new Form2();
frmForm2.Parent = this;
frmForm2.Show();
// Wait 2 seconds on the main thread
Thread.Sleep(2000);
}
private void button3_Click(object sender, System.EventArgs e)
{
// Starts a new thread
Thread thrThread = new Thread(new ThreadStart(Thread_CallBack));
thrThread.Start();
// Wait 2 seconds on the main thread
Thread.Sleep(2000);
}
private void Thread_CallBack()
{
Form2 frmForm2 = new Form2();
// frmForm2.Show(); // Can't be used the .Show method because it
doesn't suspend the execution of the thread
// and after the thread-exit the istance is
automatically set to null and the form disappear
Application.Run(frmForm2);
}
private void button4_Click(object sender, System.EventArgs e)
{
// Starts a new thread
Thread thrThread = new Thread(new
ThreadStart(ParentThread_CallBack));
thrThread.Start();
// Wait 2 seconds on the main thread
Thread.Sleep(2000);
}
private void ParentThread_CallBack()
{
// Create a new Form2 instance
Form2 frmForm2 = new Form2();
// Set the .Parent property (this is doing by the second thread)
frmForm2.Parent = this; // Crash!!!!
Application.Run(frmForm2);
}
private void button5_Click(object sender, System.EventArgs e)
{
// Exit if already instanced
if (frmThread != null) return;
// Starts a new thread
Thread thrThread = new Thread(new
ThreadStart(ParentThread2_CallBack));
thrThread.Start();
// Wait until new form instance is created by the started thread,
then continue
while (frmThread == null)
Thread.Sleep(50);
// Set the .Parent property (this is doing by the main thread)
frmThread.Parent = this;
// Wait 2 seconds on the main thread
Thread.Sleep(2000);
}
private void ParentThread2_CallBack()
{
// Create a new Form2 instance.
frmThread = new Form2();
// Wait until the main thread sets the .Parent property, then
continue
while (frmThread.Parent == null)
Thread.Sleep(50);
Application.Run(frmThread);
}
}
}
******* Form 2 *******
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace FormsAndThreads
{
/// <summary>
/// Summary description for Form2.
/// </summary>
public class Form2 : System.Windows.Forms.Form
{
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.Label label1;
private int counter = 0;
public Form2()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
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.timer1 = new System.Windows.Forms.Timer();
this.label1 = new System.Windows.Forms.Label();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// label1
//
this.label1.Font = new System.Drawing.Font("Microsoft Sans
Serif", 48F, System.Drawing.FontStyle.Regular);
this.label1.Location = new System.Drawing.Point(40, 40);
this.label1.Size = new System.Drawing.Size(96, 72);
this.label1.Text = "0";
this.label1.TextAlign =
System.Drawing.ContentAlignment.TopCenter;
//
// Form2
//
this.BackColor =
System.Drawing.Color.FromArgb(((System.Byte)(224)),
((System.Byte)(224)), ((System.Byte)(224)));
this.ClientSize = new System.Drawing.Size(178, 164);
this.Controls.Add(this.label1);
this.Text = "Form2";
this.Load += new System.EventHandler(this.Form2_Load);
this.Closed += new System.EventHandler(this.Form2_Closed);
}
#endregion
private void timer1_Tick(object sender, System.EventArgs e)
{
counter = counter + 1;
if (counter > 10) counter = 0;
label1.Text = counter.ToString();
}
private void Form2_Load(object sender, System.EventArgs e)
{
// Force the screen visualization
this.Visible = true;
this.Refresh();
this.BringToFront();
this.Focus();
}
private void Form2_Closed(object sender, System.EventArgs e)
{
timer1.Enabled = false;
}
}
}
Comments:
Form2 is a small form that has a timer that shows sequence numbers fro
0 to 9 every 100 ms. When you close it via the close button the timer
is disabled to avoid null references after the form is set to null.
Form1 is the main application window and has 5 buttons. Every button
shows a new instance of Form2 (except for button 5) in a various
manner. After shows the window, every button makkes the main thread
sleep for 2 seconds (emulating a strong data elaboration for the main
thread).
Look at the code:
button1_Click: create a new (local) instance of Form2 and shows it as
self window (visible also in the taskbar; TaskManager shows Form1 and
Form2 in "active applications" list). New Form2 instance is locked for
2 seconds, until main thread Sleep() function return. That's why new
Form2 instance is instanced in the main thread, that's sleeping.
button2_Click: create a new (local) instance of Form2 and before shows
it, it set his .Parent proprerty to "this". The new Form2 is
incorporated (owned) in the main application window, as if it were a
MDIChild. The new Form2 is no more visible in the taskbar and
TaskManager shows only Form1 in "active applications" list. New Form2
instance is also locked for 2 seconds, until main thread Sleep()
function return. That's why new Form2 instance is instanced in the main
thread, that's sleeping.
button3_Click: starts a new thread and sleep immediatelly for 2
seconds. This sleep let the new thread start, creating a new (local)
instance of Form2 and showing it as self window (visible also in the
taskbar; TaskManager shows Form1 and Form2 in "active applications"
list). New Form2 instance starts immediatelly to show the numers
sequence, even if the main thread is still sleeping. In fact, the new
Form2 window has the focus (his title bar is blue), but also the main
application window is blue, but only for 2 more seconds (remember it's
sleeping). After that time, the main application window can process his
WindowMessage queue and disable the title bar. This is the
demonstration that the two thread are (obviously) independent each
other.
This is the right way: a WaitingForm that can do his work in a
independent way from the main (hard-working) thread. Now I want to
"incorporate" the Form2 in Form1like button2_Click does: HERE STARTS
THE PROBLEM.
Watch at button4_Click code: it's the "sum" of button3_Click and
button2_Click code. Setting the .Parent property in the second thread
code, to a window reference that runs in the main thread, raises a
System.ArgumentException error.
IS ANYBODY ABLE TO EXPLAIN ME WHY THIS HAPPENS? What are the limits of
using controls, properties, "shared" variabile across multiple threads?
I know (after watch at some threads examples - first of all
SplashScreen from Microsoft) I've understand tha the calls across
multimple thread must been "safe", and must be doing with the Invoke
method.
So, now, watch at button5_Click code: frmThread is declared at class
level, not at function level, so the object is visible to button5_Click
but also to ParentThread2_CallBack functions.. However, in this way we
can have only one instance of Form2, but this is another problem.
What I do is (on button5_Click) to start a new thread and imediatelly
sleep until the frmThread object is instanced from the started thread.
The sleep is made into a while cycle, with a interval of 50 ms, so the
lock-mechanism is quite quick. The sleep in the main thread let the
second thread start, so can instance the frmThread object; after doing
that, it wait for the .Parent property to be set by the main thread.
The main thread return from his Sleep and see that frmThread object was
set, so continue with the executing of his code, setting the .Parent
property to "this", then sleeps for 2 more seconds. This new sleep let
the started thread to see that .Parent property was set by main thread
and shows the Form2 (common) instance. Thah works!
IS ANYBODY ABLE TO EXPLAIN ME WHY THIS WAY WORKS?
In detail, when the new Form2 instance is displayed in the main
application window, the counter don't start immediatelly, but wait 2
seconds, until the main thread exits from the sleep, but it would not
have to happen in a thread-programming. Can be that "incorporating" the
Form2 in Form1 causes the Form2 message queue be "attached" to Form1
message queue?
However, once the counter starts no more locks happens. In fact, after
the counter starts, try to click Button 2: you can see that main thread
(and his new Form2 instance) stops for 2 seconds, but old Button5
generated window (in a second thread) runs without stops.
NOW, WHAT IS THE RIGHT WAY TO USE WINDOWS AND CONTROLS ACROSS MULTIPLE
THREAD. HOW CAN BE METHOS, PROPERTY AND OBJECT REFERENCE PASSED IN A
SAFE WAY?
Thanks to all that can give me some explanations,
and thanks to all other people that are arrived to the end of this
(very long) post.
Massimo
I'm developing a .NET application with CF 1.0 on a x86 WinCE 4.2
device. The application must do some long elaborations, so I want to
display a foreground form, with an animation (a clock, a running car, a
moving butterfly..) that runs until the elaborations finished, so user
can have the idea that when somthing is moving, the PC is working...
I've just implemented the "WaitingForm" with a timer that displays
images sequence that do the animation (20 bitmaps images). That's works
fine. However, if I create the instance and then .Show() the instance
in the main thread, when I start the elaboration the animation become
"in jerks" and stops when I make some syncronous PInvoke calls to
external dll or when I access data etc..
So I've moved the instancing of the WaitingForm in a second thread, so
the WaitingForm has its own MessageQueue and is independent from the
main thread. Thats works fine and the animation is very fluid and
without locks (except when I move the mousepointer over the bitmpa
area.. WHY?!?!).
However, when I show the WaitingForm, it is displayed as a new window,
also in the taskbar, so, if the user switchs to other windows or to the
main application window (wich is working), the WaitingForm disappers
under the new activated window, and the animation is no more visible.
So, I can try to use the OnTop property (does it exists in WinCE?) but
what I'm trying to do is to "incorporate" the WaitingForm in the main
application window, like if it were a standard form control, placed on
the surface of the main application window (a sort of MDI-MDIChild
application). I now I can do it setting the WaitingForm .Parent
property to the main application window reference (this).
However this way raieses an exception, and I'm investigating about
this. Please, start a new VS2003 Smart Device C# project, add Form1 and
Form2 forms and cut&past the following code:
************ Form1.cs ***************
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.Threading;
namespace FormsAndThreads
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.Button button5;
private System.Windows.Forms.Button button3;
private Form2 frmThread = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
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.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.button3 = new System.Windows.Forms.Button();
this.button4 = new System.Windows.Forms.Button();
this.button5 = new System.Windows.Forms.Button();
//
// button1
//
this.button1.Location = new System.Drawing.Point(20, 16);
this.button1.Size = new System.Drawing.Size(112, 60);
this.button1.Text = "Normal";
this.button1.Click += new
System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(20, 88);
this.button2.Size = new System.Drawing.Size(112, 60);
this.button2.Text = "Parent";
this.button2.Click += new
System.EventHandler(this.button2_Click);
//
// button3
//
this.button3.Location = new System.Drawing.Point(20, 160);
this.button3.Size = new System.Drawing.Size(112, 60);
this.button3.Text = "Thread";
this.button3.Click += new
System.EventHandler(this.button3_Click);
//
// button4
//
this.button4.Location = new System.Drawing.Point(20, 232);
this.button4.Size = new System.Drawing.Size(112, 60);
this.button4.Text = "Parent Thread";
this.button4.Click += new
System.EventHandler(this.button4_Click);
//
// button5
//
this.button5.Location = new System.Drawing.Point(20, 308);
this.button5.Size = new System.Drawing.Size(112, 60);
this.button5.Text = "Parent Thread 2";
this.button5.Click += new
System.EventHandler(this.button5_Click);
//
// Form1
//
this.Controls.Add(this.button5);
this.Controls.Add(this.button4);
this.Controls.Add(this.button3);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Load(object sender, System.EventArgs e)
{
}
private void button1_Click(object sender, System.EventArgs e)
{
// Create a new Form2 instance
Form2 frmForm2 = new Form2();
frmForm2.Show();
// Wait 2 seconds on the main thread
Thread.Sleep(2000);
}
private void button2_Click(object sender, System.EventArgs e)
{
// Create a new Form2 instance and "incorporate" it in Form1
(looks like MDI-MDI child applications)
Form2 frmForm2 = new Form2();
frmForm2.Parent = this;
frmForm2.Show();
// Wait 2 seconds on the main thread
Thread.Sleep(2000);
}
private void button3_Click(object sender, System.EventArgs e)
{
// Starts a new thread
Thread thrThread = new Thread(new ThreadStart(Thread_CallBack));
thrThread.Start();
// Wait 2 seconds on the main thread
Thread.Sleep(2000);
}
private void Thread_CallBack()
{
Form2 frmForm2 = new Form2();
// frmForm2.Show(); // Can't be used the .Show method because it
doesn't suspend the execution of the thread
// and after the thread-exit the istance is
automatically set to null and the form disappear
Application.Run(frmForm2);
}
private void button4_Click(object sender, System.EventArgs e)
{
// Starts a new thread
Thread thrThread = new Thread(new
ThreadStart(ParentThread_CallBack));
thrThread.Start();
// Wait 2 seconds on the main thread
Thread.Sleep(2000);
}
private void ParentThread_CallBack()
{
// Create a new Form2 instance
Form2 frmForm2 = new Form2();
// Set the .Parent property (this is doing by the second thread)
frmForm2.Parent = this; // Crash!!!!
Application.Run(frmForm2);
}
private void button5_Click(object sender, System.EventArgs e)
{
// Exit if already instanced
if (frmThread != null) return;
// Starts a new thread
Thread thrThread = new Thread(new
ThreadStart(ParentThread2_CallBack));
thrThread.Start();
// Wait until new form instance is created by the started thread,
then continue
while (frmThread == null)
Thread.Sleep(50);
// Set the .Parent property (this is doing by the main thread)
frmThread.Parent = this;
// Wait 2 seconds on the main thread
Thread.Sleep(2000);
}
private void ParentThread2_CallBack()
{
// Create a new Form2 instance.
frmThread = new Form2();
// Wait until the main thread sets the .Parent property, then
continue
while (frmThread.Parent == null)
Thread.Sleep(50);
Application.Run(frmThread);
}
}
}
******* Form 2 *******
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace FormsAndThreads
{
/// <summary>
/// Summary description for Form2.
/// </summary>
public class Form2 : System.Windows.Forms.Form
{
private System.Windows.Forms.Timer timer1;
private System.Windows.Forms.Label label1;
private int counter = 0;
public Form2()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
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.timer1 = new System.Windows.Forms.Timer();
this.label1 = new System.Windows.Forms.Label();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// label1
//
this.label1.Font = new System.Drawing.Font("Microsoft Sans
Serif", 48F, System.Drawing.FontStyle.Regular);
this.label1.Location = new System.Drawing.Point(40, 40);
this.label1.Size = new System.Drawing.Size(96, 72);
this.label1.Text = "0";
this.label1.TextAlign =
System.Drawing.ContentAlignment.TopCenter;
//
// Form2
//
this.BackColor =
System.Drawing.Color.FromArgb(((System.Byte)(224)),
((System.Byte)(224)), ((System.Byte)(224)));
this.ClientSize = new System.Drawing.Size(178, 164);
this.Controls.Add(this.label1);
this.Text = "Form2";
this.Load += new System.EventHandler(this.Form2_Load);
this.Closed += new System.EventHandler(this.Form2_Closed);
}
#endregion
private void timer1_Tick(object sender, System.EventArgs e)
{
counter = counter + 1;
if (counter > 10) counter = 0;
label1.Text = counter.ToString();
}
private void Form2_Load(object sender, System.EventArgs e)
{
// Force the screen visualization
this.Visible = true;
this.Refresh();
this.BringToFront();
this.Focus();
}
private void Form2_Closed(object sender, System.EventArgs e)
{
timer1.Enabled = false;
}
}
}
Comments:
Form2 is a small form that has a timer that shows sequence numbers fro
0 to 9 every 100 ms. When you close it via the close button the timer
is disabled to avoid null references after the form is set to null.
Form1 is the main application window and has 5 buttons. Every button
shows a new instance of Form2 (except for button 5) in a various
manner. After shows the window, every button makkes the main thread
sleep for 2 seconds (emulating a strong data elaboration for the main
thread).
Look at the code:
button1_Click: create a new (local) instance of Form2 and shows it as
self window (visible also in the taskbar; TaskManager shows Form1 and
Form2 in "active applications" list). New Form2 instance is locked for
2 seconds, until main thread Sleep() function return. That's why new
Form2 instance is instanced in the main thread, that's sleeping.
button2_Click: create a new (local) instance of Form2 and before shows
it, it set his .Parent proprerty to "this". The new Form2 is
incorporated (owned) in the main application window, as if it were a
MDIChild. The new Form2 is no more visible in the taskbar and
TaskManager shows only Form1 in "active applications" list. New Form2
instance is also locked for 2 seconds, until main thread Sleep()
function return. That's why new Form2 instance is instanced in the main
thread, that's sleeping.
button3_Click: starts a new thread and sleep immediatelly for 2
seconds. This sleep let the new thread start, creating a new (local)
instance of Form2 and showing it as self window (visible also in the
taskbar; TaskManager shows Form1 and Form2 in "active applications"
list). New Form2 instance starts immediatelly to show the numers
sequence, even if the main thread is still sleeping. In fact, the new
Form2 window has the focus (his title bar is blue), but also the main
application window is blue, but only for 2 more seconds (remember it's
sleeping). After that time, the main application window can process his
WindowMessage queue and disable the title bar. This is the
demonstration that the two thread are (obviously) independent each
other.
This is the right way: a WaitingForm that can do his work in a
independent way from the main (hard-working) thread. Now I want to
"incorporate" the Form2 in Form1like button2_Click does: HERE STARTS
THE PROBLEM.
Watch at button4_Click code: it's the "sum" of button3_Click and
button2_Click code. Setting the .Parent property in the second thread
code, to a window reference that runs in the main thread, raises a
System.ArgumentException error.
IS ANYBODY ABLE TO EXPLAIN ME WHY THIS HAPPENS? What are the limits of
using controls, properties, "shared" variabile across multiple threads?
I know (after watch at some threads examples - first of all
SplashScreen from Microsoft) I've understand tha the calls across
multimple thread must been "safe", and must be doing with the Invoke
method.
So, now, watch at button5_Click code: frmThread is declared at class
level, not at function level, so the object is visible to button5_Click
but also to ParentThread2_CallBack functions.. However, in this way we
can have only one instance of Form2, but this is another problem.
What I do is (on button5_Click) to start a new thread and imediatelly
sleep until the frmThread object is instanced from the started thread.
The sleep is made into a while cycle, with a interval of 50 ms, so the
lock-mechanism is quite quick. The sleep in the main thread let the
second thread start, so can instance the frmThread object; after doing
that, it wait for the .Parent property to be set by the main thread.
The main thread return from his Sleep and see that frmThread object was
set, so continue with the executing of his code, setting the .Parent
property to "this", then sleeps for 2 more seconds. This new sleep let
the started thread to see that .Parent property was set by main thread
and shows the Form2 (common) instance. Thah works!
IS ANYBODY ABLE TO EXPLAIN ME WHY THIS WAY WORKS?
In detail, when the new Form2 instance is displayed in the main
application window, the counter don't start immediatelly, but wait 2
seconds, until the main thread exits from the sleep, but it would not
have to happen in a thread-programming. Can be that "incorporating" the
Form2 in Form1 causes the Form2 message queue be "attached" to Form1
message queue?
However, once the counter starts no more locks happens. In fact, after
the counter starts, try to click Button 2: you can see that main thread
(and his new Form2 instance) stops for 2 seconds, but old Button5
generated window (in a second thread) runs without stops.
NOW, WHAT IS THE RIGHT WAY TO USE WINDOWS AND CONTROLS ACROSS MULTIPLE
THREAD. HOW CAN BE METHOS, PROPERTY AND OBJECT REFERENCE PASSED IN A
SAFE WAY?
Thanks to all that can give me some explanations,
and thanks to all other people that are arrived to the end of this
(very long) post.
Massimo