[Mono-devel-list] Re: Threading model

Rafael Teixeira monoman at gmail.com
Fri Aug 5 14:00:55 EDT 2005


Hi Paul,

First, from msdn:

Note   There are four methods on a control that are safe to call from
any thread: Invoke, BeginInvoke, EndInvoke, and CreateGraphics. For
all other method calls, you should use one of the invoke methods to
marshal the call to the control's thread.

See: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformscontrolclassinvoketopic.asp
and
http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemWindowsFormsControlClassInvokeRequiredTopic.asp?frame=true

This modified version of your ugly program runs for a while (hundreds
of increments):
<code>
using System;
using System.Threading;
using System.Windows.Forms;
using System.Drawing;

public class Sharing1 : Form
{
 private TextBox accessCountBox = new TextBox();
 private Button start = new Button();
 private Button watch = new Button();
 private int accessCount = 0;

 public void incrementAccess()
 {
   accessCount++;
   UpdateAccess();
 }

 public void UpdateAccess()
 {
 	if (!accessCountBox.InvokeRequired)
	   accessCountBox.Text = accessCount.ToString();
	else
	   accessCountBox.BeginInvoke(new MethodInvoker(UpdateAccess));
 }

 private int numCounters = 12;
 private int numWatchers = 15;
 private TwoCounter[] s;

 public Sharing1()
 {
   ClientSize = new Size(450, 480);
   Panel p = new Panel();
   p.Size = new Size(400, 50);
   start.Click += new EventHandler(StartAllThreads);
   watch.Click += new EventHandler(StartAllWatchers);

   accessCountBox.Text = "0";
   accessCountBox.Location = new Point(10, 10);
   start.Text = "Start threads";
   start.Location = new Point(110, 10);
   start.Width = 80;
   watch.Text = "Begin watching";
   watch.Location = new Point(210, 10);
   watch.Width = 180;

   p.Controls.Add(start);
   p.Controls.Add(watch);
   p.Controls.Add(accessCountBox);

   s = new TwoCounter[numCounters];
   for (int i = 0; i < s.Length; i++)
   {
     s[i] = new TwoCounter(new TwoCounter.IncrementAccess(incrementAccess));
     s[i].Location = new Point(10, 50 + s[i].Height * i);
     Controls.Add(s[i]);
   }

   this.Closed += new EventHandler(StopAllThreads);
   Controls.Add(p);
 }

 public void StartAllThreads(object sender, EventArgs ea)
 {
   for (int i = 0; i < s.Length; i++)
     s[i].Start();
 }

 public void StopAllThreads(object sender, EventArgs ea)
 {
   for (int i = 0; i < s.Length; i++)
     if (s[i] != null)
       s[i].Stop();
 }

 public void StartAllWatchers(object sender, EventArgs ea)
 {
   for (int i = 0; i < numWatchers; i++)
     new Watcher(s);
 }

 public static void Main(string [] args)
 {
   Sharing1 app = new Sharing1();
   if (args.Length > 0)
   {
     app.numCounters = SByte.Parse(args[0]);
     if (args.Length == 2)
       app.numCounters = SByte.Parse(args[1]);
   }
   Application.Run(app);
 }
}

class TwoCounter : Panel
{
 private bool started = false;
 private Label t1;
 private Label t2;
 private Label lbl;
 private Thread t;

 private int count1 = 0, count2 = 0;
 public delegate void IncrementAccess();
 IncrementAccess del;

 public TwoCounter(IncrementAccess del)
 {
   this.del = del;
   this.Size = new Size(350, 30);
   this.BorderStyle = BorderStyle.Fixed3D;
   t1 = new Label();
   t1.Location = new Point(10, 10);
   t2 = new Label();
   t2.Location = new Point(110, 10);
   lbl = new Label();
   lbl.Location = new Point(210, 10);
   lbl.Text = "Count1 == Count2";
   Controls.AddRange(new Control[] {t1, t2, lbl} );

   t = new Thread(new ThreadStart(run));
 }

 public void Start()
 {
   if (!started)
   {
     started = true;
     t.Start();
   }
 }

 public void Stop()
 {
   t.Abort();
 }

 public void run()
 {
   while(true)
   {
	 UpdateLabels();
     Thread.Sleep(500);
   }
 }
 
 public void synchTest()
 {
   del();
   if (count1 != count2)
   	UpdateSynch();
 }
 
 private void UpdateLabels()
 {
 	if (!t1.InvokeRequired) {
     t1.Text = (++count1).ToString();
     t2.Text = (++count2).ToString(); 	
   } else  // Will be queued for execution in the UI Thread 
   		t1.BeginInvoke(new MethodInvoker(UpdateLabels));
 }
 
 private void UpdateSynch()
 {
 	if (!lbl.InvokeRequired)
     lbl.Text = "Unsynched";
    else  // Will be queued for execution in the UI Thread 
    	lbl.BeginInvoke(new MethodInvoker(UpdateSynch));
 }
 
}

class Watcher
{
 TwoCounter[] s;

 public Watcher(TwoCounter[] s)
 {
   this.s = s;
   new Thread(new ThreadStart(run)).Start();
 }

 public void run()
 {
   while(true)
   {
     for(int i = 0; i < s.Length; i++)
       s[i].synchTest();
     Thread.Sleep(500);
   }
 }
}
</code>

Probably some error on locking/freeing threads is generating this exception

<exception>
Unhandled Exception: System.ObjectDisposedException: The object was
used after being disposed.
in [0x0002d] System.Threading.WaitHandle:CheckDisposed ()
in [0x00001] (at mcs/class/corlib/System.Threading/WaitHandle.cs:177)
System.Threading.WaitHandle:WaitOne ()
in [0x0000e] (at
mcs/class/Managed.Windows.Forms/System.Windows.Forms/AsyncMethodResult.cs:78)
System.Windows.Forms.AsyncMethodResult:Complete (System.Object result)
in [0x00062] (at
mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUIDriver.cs:279)
System.Windows.Forms.XplatUIDriverSupport:ExecuteClientMessage
(GCHandle gchandle)
in [0x00a8c] (at
mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUIX11.cs:2536)
System.Windows.Forms.XplatUIX11:GetMessage (System.Windows.Forms.MSG
msg, IntPtr handle, Int32 wFilterMin, Int32 wFilterMax)
in [0x00009] (at
mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUI.cs:440)
System.Windows.Forms.XplatUI:GetMessage (System.Windows.Forms.MSG msg,
IntPtr hWnd, Int32 wFilterMin, Int32 wFilterMax)
in [0x0012e] (at
mcs/class/Managed.Windows.Forms/System.Windows.Forms/Application.cs:365)
System.Windows.Forms.Application:Run ()
in [0x0004a] (at
mcs/class/Managed.Windows.Forms/System.Windows.Forms/Application.cs:388)
System.Windows.Forms.Application:Run
(System.Windows.Forms.ApplicationContext context)
in [0x0000c] (at
mcs/class/Managed.Windows.Forms/System.Windows.Forms/Application.cs:379)
System.Windows.Forms.Application:Run (System.Windows.Forms.Form
mainForm)
in <0x000b8> Sharing1:Main (System.String[] args)
</exception>

Hope it helps,

-- 
Rafael "Monoman" Teixeira
---------------------------------------
I'm trying to become a "Rosh Gadol" before my own eyes. 
See http://www.joelonsoftware.com/items/2004/12/06.html for enlightment.
It hurts!



More information about the Mono-devel-list mailing list