Operations on Lock Threading
Operations on Lock Threading
Managing a Lock
Synchronizing a Lock
When creating a lock using the Monitor class, both the Monitor.Enter() and the Monitor.Exit() methods must use the same object. If they don't, the application would throw and exception named SynchronizationLockException:
public class SynchronizationLockException : SystemException
Most of the times, this error should be very to deal with. Most of the times (but not always), the error indicates that you are using different keys in the beginning (Monitor.Enter()) and the end (Monitor.Exit()) and your critical section, which should be easy to fix by checking their names (or objects).
Requesting a Lock
We saw that you can (and should) first create locks before assigning them to threads. This makes it possible to use a different lock for each critical section. As a matter of fact, a thread can use different locks, one at a time. This means that a thread can use a lock A to perform an operation, release that lock A and grab another lock B to perform another operation, release that lock B, and start using another lock C or re-use a previously released lock A or B for another operation. The thread you are using at one particular time is called the current thread (in previous lessons, we already saw how to get a reference to the current thread as the Thread.CurrentThread property). Here is an example where the current thread is using two different locks at different times:
Class: Circle.cs:
using System; using System.Drawing; public enum MovingDirection { Right, Left, Down, Up } public class Circle { public float X { get; set; } public float Y { get; set; } public Color Color { get; set; } public MovingDirection Direction { get; set; } public void Draw(Graphics graph) { graph.DrawEllipse(new Pen(this.Color, 1.00F), X, Y, 100.0F, 100.0F); } }
Class: Monitoring.cs
using System; using System.Drawing; using System.Threading; using System.Windows.Forms; public class CirclesAndLines : Form { private PictureBox pbxCanvas; private System.Windows.Forms.Timer tmrControlCircles; private Circle[] circles = new Circle[2]; object concierge = new object(); object usher = new object(); public CirclesAndLines() { InitializeComponent(); } private void InitializeComponent() { // Picture Box: Canvas pbxCanvas = new PictureBox(); pbxCanvas.Paint += pbxCanvasPaint; pbxCanvas.Location = new Point(0, 0); Controls.Add(pbxCanvas); // Timer: Circles Manager tmrControlCircles = new System.Windows.Forms.Timer(); tmrControlCircles.Enabled = true; tmrControlCircles.Interval = 10; tmrControlCircles.Tick += tmrControlCirclesTick; // Form: Exercise MaximizeBox = false; Load += ExerciseLoad; Text = "Animation - Circles"; ClientSize = new System.Drawing.Size(800, 680); StartPosition = FormStartPosition.CenterScreen; pbxCanvas.Dock = DockStyle.Fill; } private void ExerciseLoad(object sender, EventArgs e) { circles[0] = new Circle(); circles[0].X = 0.00f; circles[0].Color = Color.Lime; circles[0].Direction = MovingDirection.Right; circles[0].Y = (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F; circles[1] = new Circle(); circles[1].Color = Color.Red; circles[1].Direction = MovingDirection.Left; circles[1].X = pbxCanvas.ClientRectangle.Width - 100.00F; circles[1].Y = (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F; } private void CheckLeftCircle() { Monitor.Enter(concierge); try { if (circles[0].X < 0.00F) { circles[0].X = 0.00F; circles[0].Direction = MovingDirection.Right; } if (circles[0].X > (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F) { circles[0].X = (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F; circles[0].Direction = MovingDirection.Left; } if (circles[0].Direction == MovingDirection.Right) circles[0].X++; else circles[0].X--; } finally { Monitor.Exit(concierge); } } private void CheckRightCircle() { Monitor.Enter(concierge); try { if (circles[1].X < (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F) { circles[1].X = (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F; circles[1].Direction = MovingDirection.Right; } if (circles[1].X > (pbxCanvas.ClientRectangle.Width - 100.00F)) { circles[1].X = (pbxCanvas.ClientRectangle.Width - 100.00F); circles[1].Direction = MovingDirection.Left; } if (circles[1].Direction == MovingDirection.Right) circles[1].X++; else circles[1].X--; } finally { Monitor.Exit(concierge); } } private void pbxCanvasPaint(object sender, PaintEventArgs e) { e.Graphics.FillRectangle(Brushes.Black, 0.00F, 0.00F, pbxCanvas.ClientRectangle.Width, pbxCanvas.ClientRectangle.Height); CheckLeftCircle(); CheckRightCircle(); circles[0].Draw(e.Graphics); circles[1].Draw(e.Graphics); } private void tmrControlCirclesTick(object sender, EventArgs e) { pbxCanvas.Invalidate(); } [STAThread] public static int Main() { Application.EnableVisualStyles(); Application.Run(new CirclesAndLines()); return 0; } }
When a critical section grabs a lock, no other critical section should use the same lock, until the previous critical section ends. This means that you should not assign a lock that one thread is currently using to another section. To help you find out whether a lock is available before using it, the Monitor class provides an overloaded method named TryEnter. One of its versions uses the following syntax:
public static bool TryEnter(object obj)
This method takes a lock as argument and tries to start a critical section with it. If the method is able to get the lock, the method returns true. Here is an example of calling this method:
using System; using System.Drawing; using System.Threading; using System.Windows.Forms; public class CirclesAndLines : Form { private PictureBox pbxCanvas; private System.Windows.Forms.Timer tmrControlCircles; private Circle[] circles = new Circle[2]; object concierge = new object(); object usher = new object(); public CirclesAndLines() { InitializeComponent(); } private void InitializeComponent() { // Picture Box: Canvas pbxCanvas = new PictureBox(); pbxCanvas.Paint += pbxCanvasPaint; pbxCanvas.Location = new Point(0, 0); Controls.Add(pbxCanvas); // Timer: Circles Manager tmrControlCircles = new System.Windows.Forms.Timer(); tmrControlCircles.Enabled = true; tmrControlCircles.Interval = 10; tmrControlCircles.Tick += tmrControlCirclesTick; // Form: Exercise MaximizeBox = false; Load += ExerciseLoad; Text = "Animation - Circles"; ClientSize = new System.Drawing.Size(800, 680); StartPosition = FormStartPosition.CenterScreen; pbxCanvas.Dock = DockStyle.Fill; } private void ExerciseLoad(object sender, EventArgs e) { circles[0] = new Circle(); circles[0].X = 0.00f; circles[0].Color = Color.Lime; circles[0].Direction = MovingDirection.Right; circles[0].Y = (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F; circles[1] = new Circle(); circles[1].Color = Color.Red; circles[1].Direction = MovingDirection.Left; circles[1].X = pbxCanvas.ClientRectangle.Width - 100.00F; circles[1].Y = (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F; } private void CheckLeftCircle() { Monitor.Enter(concierge); try { if (circles[0].X < 0.00F) { circles[0].X = 0.00F; circles[0].Direction = MovingDirection.Right; } if (circles[0].X > (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F) { circles[0].X = (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F; circles[0].Direction = MovingDirection.Left; } if (circles[0].Direction == MovingDirection.Right) circles[0].X++; else circles[0].X--; } finally { Monitor.Exit(concierge); } } private void CheckRightCircle() { if (Monitor.TryEnter(concierge)) { try { if (circles[1].X < (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F) { circles[1].X = (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F; circles[1].Direction = MovingDirection.Right; } if (circles[1].X > (pbxCanvas.ClientRectangle.Width - 100.00F)) { circles[1].X = (pbxCanvas.ClientRectangle.Width - 100.00F); circles[1].Direction = MovingDirection.Left; } if (circles[1].Direction == MovingDirection.Right) circles[1].X++; else circles[1].X--; } finally { Monitor.Exit(concierge); } } } private void pbxCanvasPaint(object sender, PaintEventArgs e) { e.Graphics.FillRectangle(Brushes.Black, 0.00F, 0.00F, pbxCanvas.ClientRectangle.Width, pbxCanvas.ClientRectangle.Height); CheckLeftCircle(); CheckRightCircle(); circles[0].Draw(e.Graphics); circles[1].Draw(e.Graphics); } private void tmrControlCirclesTick(object sender, EventArgs e) { pbxCanvas.Invalidate(); } [STAThread] public static int Main() { Application.EnableVisualStyles(); Application.Run(new CirclesAndLines()); return 0; } }
If the Monitor.TryEnter(object obj) method cannot get the lock for any reason, it returns false. To let you find out whether the lock was acquired or not, the Monitor class provides the following version of the TryEnter method:
public static void TryEnter(object obj, ref bool lockTaken)
In this case, the lockTaken argument passed by reference returns a value that indicates the outcome of grabbing the lock. Here is an example of calling this version of the method:
using System; using System.Drawing; using System.Threading; using System.Windows.Forms; public class CirclesAndLines : Form { private PictureBox pbxCanvas; private System.Windows.Forms.Timer tmrControlCircles; object usher = new object(); Label reserved = new Label(); object concierge = new object(); Control protection = new Control(); private Circle[] circles = new Circle[4]; public CirclesAndLines() { InitializeComponent(); } private void InitializeComponent() { // Picture Box: Canvas pbxCanvas = new PictureBox(); pbxCanvas.Paint += pbxCanvasPaint; pbxCanvas.Location = new Point(0, 0); Controls.Add(pbxCanvas); // Timer: Circles Manager tmrControlCircles = new System.Windows.Forms.Timer(); tmrControlCircles.Enabled = true; tmrControlCircles.Interval = 10; tmrControlCircles.Tick += tmrControlCirclesTick; // Form: Exercise MaximizeBox = false; Load += ExerciseLoad; Text = "Animation - Circles"; ClientSize = new System.Drawing.Size(800, 680); StartPosition = FormStartPosition.CenterScreen; pbxCanvas.Dock = DockStyle.Fill; } private void ExerciseLoad(object sender, EventArgs e) { Monitor.Enter(usher); bool lockTaken = false; Monitor.Enter(reserved); Monitor.Enter(concierge); Monitor.TryEnter(protection, ref lockTaken); try { circles[0] = new Circle(); circles[0].X = 0.00f; circles[0].Color = Color.Lime; circles[0].Direction = MovingDirection.Right; circles[0].Y = (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F; } finally { Monitor.Exit(concierge); } if (Monitor.TryEnter(usher) == true) { try { circles[1] = new Circle(); circles[1].Color = Color.Red; circles[1].Direction = MovingDirection.Left; circles[1].X = pbxCanvas.ClientRectangle.Width - 100.00F; circles[1].Y = (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F; } finally { Monitor.Exit(usher); } } try { circles[2] = new Circle(); circles[2].Color = Color.Yellow; circles[2].Direction = MovingDirection.Down; circles[2].X = (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F; circles[2].Y = (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F; } finally { Monitor.Exit(reserved); } if (lockTaken == true) { try { circles[3] = new Circle(); circles[3].Color = Color.Olive; circles[3].Direction = MovingDirection.Up; circles[3].X = (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F; circles[3].Y = (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F; } finally { Monitor.Exit(protection); } } } private void pbxCanvasPaint(object sender, PaintEventArgs e) { Monitor.Enter(usher); bool lockTaken = false; Monitor.Enter(reserved); Monitor.Enter(concierge); Monitor.TryEnter(protection, ref lockTaken); e.Graphics.FillRectangle(Brushes.Black, 0.00F, 0.00F, pbxCanvas.ClientRectangle.Width, pbxCanvas.ClientRectangle.Height); try { if (circles[0].X < 0.00F) { circles[0].X = 0.00F; circles[0].Color = Color.Lime; circles[0].Direction = MovingDirection.Right; } if (circles[0].X > (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F) { circles[0].Color = Color.Orange; circles[0].Direction = MovingDirection.Left; circles[0].X = (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F; } if (circles[0].Direction == MovingDirection.Right) circles[0].X++; else circles[0].X--; circles[0].Draw(e.Graphics); } finally { Monitor.Exit(concierge); } if (Monitor.TryEnter(usher) == true) { try { if (circles[1].X < (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F) { circles[1].Color = Color.Red; circles[1].Direction = MovingDirection.Right; circles[1].X = (pbxCanvas.ClientRectangle.Width - 100.00F) / 2.00F; } if (circles[1].X > (pbxCanvas.ClientRectangle.Width - 100.00F)) { circles[1].Color = Color.AliceBlue; circles[1].Direction = MovingDirection.Left; circles[1].X = (pbxCanvas.ClientRectangle.Width - 100.00F); } if (circles[1].Direction == MovingDirection.Right) circles[1].X++; else circles[1].X--; circles[1].Draw(e.Graphics); } finally { Monitor.Exit(usher); } } try { if (circles[2].Y < 0.00F) { circles[2].Y = 0.00F; circles[2].Color = Color.Yellow; circles[2].Direction = MovingDirection.Down; } if (circles[2].Y > (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F) { circles[2].Color = Color.Aqua; circles[2].Direction = MovingDirection.Up; circles[2].Y = (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F; } if (circles[2].Direction == MovingDirection.Down) circles[2].Y++; else circles[2].Y--; circles[2].Draw(e.Graphics); } finally { Monitor.Exit(reserved); } if (lockTaken == true) { try { if (circles[3].Y < ((pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F)) { circles[3].Color = Color.DarkOliveGreen; circles[3].Direction = MovingDirection.Down; circles[3].Y = (pbxCanvas.ClientRectangle.Height - 100.00F) / 2.00F; } if (circles[3].Y > (pbxCanvas.ClientRectangle.Height - 100.00F)) { circles[3].Color = Color.Chocolate; circles[3].Direction = MovingDirection.Up; circles[3].Y = pbxCanvas.ClientRectangle.Height - 100.00F; } if (circles[3].Direction == MovingDirection.Down) circles[3].Y++; else circles[3].Y--; circles[3].Draw(e.Graphics); } finally { Monitor.Exit(protection); } } } private void tmrControlCirclesTick(object sender, EventArgs e) { pbxCanvas.Invalidate(); } [STAThread] public static int Main() { Application.EnableVisualStyles(); Application.Run(new CirclesAndLines()); return 0; } }
In any case, if the method fails in accessing the lock, you can let the thread wait a certain time before trying agin. To suport this, the Monitor class provides two versions of the TryEnter method whose syntaxes are:
public static bool TryEnter(object obj, int millisecondsTimeout) public static bool TryEnter(object obj, TimeSpan timeout)
Checking the Availability of a Lock
At any time, to let you find out whether the current thread is currently holding a certain lock, the Monitor class provides a static Boolean method named IsEntered. Its syntax is:
public static bool IsEntered(object obj)
A lock object is passed to the method to check. If the current thread is holding the loce passed as argument, the method returns true, which means the lock is not available to other threads. If the current thread is not holding that lock, the method returns false, in which case another thread is free to grab that lock. Consider the following examples of calling this method (notice the sections where the method returns true and those where the method returns false):
using System; using System.Drawing; using System.Threading; using System.Windows.Forms; using System.Drawing.Drawing2D; public class Monitoring : Form { public Monitoring() { InitializeComponent(); } private void InitializeComponent() { MaximizeBox = false; StartPosition = FormStartPosition.CenterScreen; ClientSize = new System.Drawing.Size(600, 330); Text = "Monitoring of House Design and Planning"; } protected override void OnPaint(PaintEventArgs e) { object setup = new object(); object bootCamp = new object(); object criticalDrawing = new object(); LinearGradientBrush lgbWindows = new LinearGradientBrush(new Rectangle(130, 240, 90, 40), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); Monitor.Enter(criticalDrawing); try { // Front Wall LinearGradientBrush lgbWalls = new LinearGradientBrush(new Rectangle(120, 40, 310, 90), Color.Chocolate, Color.Tan, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWalls, new Rectangle(120, 40, 310, 90)); lgbWalls = new LinearGradientBrush(new Rectangle(120, 130, 310, 90), Color.Chocolate, Color.Tan, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWalls, new Rectangle(120, 130, 310, 90)); lgbWalls = new LinearGradientBrush(new Rectangle(120, 210, 310, 90), Color.Chocolate, Color.Tan, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWalls, new Rectangle(120, 220, 310, 100)); e.Graphics.DrawRectangle(Pens.Black, new Rectangle(120, 40, 310, 280)); // Foundation HatchBrush brushDivot = new HatchBrush(HatchStyle.HorizontalBrick, Color.SaddleBrown); e.Graphics.FillRectangle(brushDivot, new Rectangle(110, 300, 330, 20)); e.Graphics.DrawRectangle(Pens.Black, new Rectangle(110, 300, 330, 20)); } finally { Monitor.Exit(criticalDrawing); } Monitor.Enter(setup); try { // Story Separators LinearGradientBrush lgbSeparators = new LinearGradientBrush(new Rectangle(120, 215, 310, 10), Color.DarkGray, Color.Silver, LinearGradientMode.Vertical); e.Graphics.FillRectangle(lgbSeparators, new Rectangle(120, 215, 310, 10)); e.Graphics.DrawRectangle(Pens.Black, new Rectangle(120, 215, 310, 10)); lgbSeparators = new LinearGradientBrush(new Rectangle(120, 125, 310, 10), Color.DarkGray, Color.Silver, LinearGradientMode.Vertical); e.Graphics.FillRectangle(lgbSeparators, new Rectangle(120, 125, 310, 10)); e.Graphics.DrawRectangle(Pens.Black, new Rectangle(120, 125, 310, 10)); } finally { Monitor.Exit(setup); } if (Monitor.IsEntered(criticalDrawing) == true) { Monitor.Enter(criticalDrawing); try { // Entrance Door lgbWindows = new LinearGradientBrush(new Rectangle(235, 240, 80, 80), Color.DarkSeaGreen, Color.Bisque, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(235, 240, 80, 80)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(235, 240, 80, 80)); e.Graphics.DrawLine(new Pen(Color.White, 2), new Point(275, 240), new Point(275, 320)); e.Graphics.DrawLine(new Pen(Color.Gray, 2), new Point(0, 320), new Point(700, 320)); } finally { Monitor.Exit(criticalDrawing); } } Monitor.Enter(bootCamp); try { // First Floor Windows lgbWindows = new LinearGradientBrush(new Rectangle(130, 240, 90, 40), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(130, 240, 90, 40)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(130, 240, 90, 40)); lgbWindows = new LinearGradientBrush(new Rectangle(330, 240, 90, 40), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(330, 240, 90, 40)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(330, 240, 90, 40)); } finally { Monitor.Exit(bootCamp); } if (Monitor.IsEntered(criticalDrawing) == true) { Monitor.Enter(criticalDrawing); try { // Second Floor Windows lgbWindows = new LinearGradientBrush(new Rectangle(130, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(130, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(130, 150, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(180, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(180, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(180, 150, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(230, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(230, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(230, 150, 40, 50)); } finally { Monitor.Exit(criticalDrawing); } } Monitor.Enter(bootCamp); try { lgbWindows = new LinearGradientBrush(new Rectangle(280, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(280, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(280, 150, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(330, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(330, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(330, 150, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(380, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(380, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(380, 150, 40, 50)); } finally { Monitor.Exit(bootCamp); } if (Monitor.IsEntered(setup) == false) { Monitor.Enter(setup); try { // Third Floor Windows lgbWindows = new LinearGradientBrush(new Rectangle(130, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(130, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(130, 60, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(180, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(180, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(180, 60, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(230, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(230, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(230, 60, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(280, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(280, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(280, 60, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(330, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(330, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(330, 60, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(380, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(380, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(380, 60, 40, 50)); } finally { Monitor.Exit(setup); } } if (Monitor.IsEntered(setup) == true) { Monitor.Enter(setup); try { // Trees LinearGradientBrush lgbTrees = new LinearGradientBrush(new Rectangle(80, 170, 30, 100), Color.DarkGreen, Color.Olive, LinearGradientMode.ForwardDiagonal); e.Graphics.DrawLine(new Pen(Color.Gray, 5), new Point(95, 200), new Point(95, 320)); e.Graphics.FillEllipse(lgbTrees, new Rectangle(80, 150, 30, 120)); e.Graphics.DrawLine(new Pen(Color.Gray, 8), new Point(10, 240), new Point(10, 320)); lgbTrees = new LinearGradientBrush(new Rectangle(-30, 210, 80, 50), Color.Olive, Color.DarkGreen, LinearGradientMode.ForwardDiagonal); e.Graphics.FillEllipse(lgbTrees, new Rectangle(-50, 200, 100, 40)); e.Graphics.DrawLine(new Pen(Color.Gray, 5), new Point(40, 220), new Point(40, 320)); lgbTrees = new LinearGradientBrush(new Rectangle(10, 240, 80, 50), Color.DarkGreen, Color.Green, LinearGradientMode.ForwardDiagonal); e.Graphics.FillEllipse(lgbTrees, new Rectangle(10, 220, 60, 80)); lgbTrees = new LinearGradientBrush(new Rectangle(20, 290, 80, 40), Color.Green, Color.Lavender, LinearGradientMode.ForwardDiagonal); e.Graphics.DrawLine(new Pen(Color.Gray, 6), new Point(70, 280), new Point(70, 320)); e.Graphics.FillEllipse(lgbTrees, new Rectangle(50, 280, 40, 20)); e.Graphics.DrawLine(new Pen(Color.Gray, 8), new Point(485, 230), new Point(485, 320)); lgbTrees = new LinearGradientBrush(new Rectangle(420, 210, 80, 50), Color.LightGreen, Color.DarkGreen, LinearGradientMode.ForwardDiagonal); e.Graphics.FillEllipse(lgbTrees, new Rectangle(440, 200, 80, 40)); e.Graphics.DrawLine(new Pen(Color.Gray, 8), new Point(520, 230), new Point(520, 320)); lgbTrees = new LinearGradientBrush(new Rectangle(500, 190, 60, 100), Color.Aquamarine, Color.DarkGreen, LinearGradientMode.ForwardDiagonal); e.Graphics.FillEllipse(lgbTrees, new Rectangle(500, 180, 40, 120)); e.Graphics.DrawLine(new Pen(Color.Gray, 12), new Point(560, 250), new Point(560, 320)); lgbTrees = new LinearGradientBrush(new Rectangle(540, 240, 80, 50), Color.Lime, Color.DarkGreen, LinearGradientMode.ForwardDiagonal); e.Graphics.FillEllipse(lgbTrees, new Rectangle(520, 240, 80, 60)); } finally { Monitor.Exit(setup); } } } public static int Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Monitoring()); return 0; } }
This would produce:
Change the code as follows:
using System; using System.Drawing; using System.Threading; using System.Windows.Forms; using System.Drawing.Drawing2D; public class Monitoring : Form { public Monitoring() { InitializeComponent(); } private void InitializeComponent() { MaximizeBox = false; StartPosition = FormStartPosition.CenterScreen; ClientSize = new System.Drawing.Size(600, 330); Text = "Monitoring of House Design and Planning"; } protected override void OnPaint(PaintEventArgs e) { object setup = new object(); object bootCamp = new object(); object criticalDrawing = new object(); LinearGradientBrush lgbWindows = new LinearGradientBrush(new Rectangle(130, 240, 90, 40), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); Monitor.Enter(criticalDrawing); try { // Front Wall LinearGradientBrush lgbWalls = new LinearGradientBrush(new Rectangle(120, 40, 310, 90), Color.Chocolate, Color.Tan, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWalls, new Rectangle(120, 40, 310, 90)); lgbWalls = new LinearGradientBrush(new Rectangle(120, 130, 310, 90), Color.Chocolate, Color.Tan, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWalls, new Rectangle(120, 130, 310, 90)); lgbWalls = new LinearGradientBrush(new Rectangle(120, 210, 310, 90), Color.Chocolate, Color.Tan, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWalls, new Rectangle(120, 220, 310, 100)); e.Graphics.DrawRectangle(Pens.Black, new Rectangle(120, 40, 310, 280)); // Foundation HatchBrush brushDivot = new HatchBrush(HatchStyle.HorizontalBrick, Color.SaddleBrown); e.Graphics.FillRectangle(brushDivot, new Rectangle(110, 300, 330, 20)); e.Graphics.DrawRectangle(Pens.Black, new Rectangle(110, 300, 330, 20)); } finally { Monitor.Exit(criticalDrawing); } Monitor.Enter(setup); try { // Story Separators LinearGradientBrush lgbSeparators = new LinearGradientBrush(new Rectangle(120, 215, 310, 10), Color.DarkGray, Color.Silver, LinearGradientMode.Vertical); e.Graphics.FillRectangle(lgbSeparators, new Rectangle(120, 215, 310, 10)); e.Graphics.DrawRectangle(Pens.Black, new Rectangle(120, 215, 310, 10)); lgbSeparators = new LinearGradientBrush(new Rectangle(120, 125, 310, 10), Color.DarkGray, Color.Silver, LinearGradientMode.Vertical); e.Graphics.FillRectangle(lgbSeparators, new Rectangle(120, 125, 310, 10)); e.Graphics.DrawRectangle(Pens.Black, new Rectangle(120, 125, 310, 10)); } finally { Monitor.Exit(setup); } if (Monitor.IsEntered(criticalDrawing) == false) { Monitor.Enter(criticalDrawing); try { // Entrance Door lgbWindows = new LinearGradientBrush(new Rectangle(235, 240, 80, 80), Color.DarkSeaGreen, Color.Bisque, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(235, 240, 80, 80)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(235, 240, 80, 80)); e.Graphics.DrawLine(new Pen(Color.White, 2), new Point(275, 240), new Point(275, 320)); e.Graphics.DrawLine(new Pen(Color.Gray, 2), new Point(0, 320), new Point(700, 320)); } finally { Monitor.Exit(criticalDrawing); } } Monitor.Enter(bootCamp); try { // First Floor Windows lgbWindows = new LinearGradientBrush(new Rectangle(130, 240, 90, 40), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(130, 240, 90, 40)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(130, 240, 90, 40)); lgbWindows = new LinearGradientBrush(new Rectangle(330, 240, 90, 40), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(330, 240, 90, 40)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(330, 240, 90, 40)); } finally { Monitor.Exit(bootCamp); } if (Monitor.IsEntered(criticalDrawing) == false) { Monitor.Enter(criticalDrawing); try { // Second Floor Windows lgbWindows = new LinearGradientBrush(new Rectangle(130, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(130, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(130, 150, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(180, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(180, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(180, 150, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(230, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(230, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(230, 150, 40, 50)); } finally { Monitor.Exit(criticalDrawing); } } Monitor.Enter(bootCamp); try { lgbWindows = new LinearGradientBrush(new Rectangle(280, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(280, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(280, 150, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(330, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(330, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(330, 150, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(380, 150, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(380, 150, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(380, 150, 40, 50)); } finally { Monitor.Exit(bootCamp); } if (Monitor.IsEntered(setup) == false) { Monitor.Enter(setup); try { // Third Floor Windows lgbWindows = new LinearGradientBrush(new Rectangle(130, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(130, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(130, 60, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(180, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(180, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(180, 60, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(230, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(230, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(230, 60, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(280, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(280, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(280, 60, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(330, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(330, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(330, 60, 40, 50)); lgbWindows = new LinearGradientBrush(new Rectangle(380, 60, 40, 50), Color.LightBlue, Color.White, LinearGradientMode.ForwardDiagonal); e.Graphics.FillRectangle(lgbWindows, new Rectangle(380, 60, 40, 50)); e.Graphics.DrawRectangle(new Pen(Color.White, 2), new Rectangle(380, 60, 40, 50)); } finally { Monitor.Exit(setup); } } if (Monitor.IsEntered(setup) == false) { Monitor.Enter(setup); try { // Trees LinearGradientBrush lgbTrees = new LinearGradientBrush(new Rectangle(80, 170, 30, 100), Color.DarkGreen, Color.Olive, LinearGradientMode.ForwardDiagonal); e.Graphics.DrawLine(new Pen(Color.Gray, 5), new Point(95, 200), new Point(95, 320)); e.Graphics.FillEllipse(lgbTrees, new Rectangle(80, 150, 30, 120)); e.Graphics.DrawLine(new Pen(Color.Gray, 8), new Point(10, 240), new Point(10, 320)); lgbTrees = new LinearGradientBrush(new Rectangle(-30, 210, 80, 50), Color.Olive, Color.DarkGreen, LinearGradientMode.ForwardDiagonal); e.Graphics.FillEllipse(lgbTrees, new Rectangle(-50, 200, 100, 40)); e.Graphics.DrawLine(new Pen(Color.Gray, 5), new Point(40, 220), new Point(40, 320)); lgbTrees = new LinearGradientBrush(new Rectangle(10, 240, 80, 50), Color.DarkGreen, Color.Green, LinearGradientMode.ForwardDiagonal); e.Graphics.FillEllipse(lgbTrees, new Rectangle(10, 220, 60, 80)); lgbTrees = new LinearGradientBrush(new Rectangle(20, 290, 80, 40), Color.Green, Color.Lavender, LinearGradientMode.ForwardDiagonal); e.Graphics.DrawLine(new Pen(Color.Gray, 6), new Point(70, 280), new Point(70, 320)); e.Graphics.FillEllipse(lgbTrees, new Rectangle(50, 280, 40, 20)); e.Graphics.DrawLine(new Pen(Color.Gray, 8), new Point(485, 230), new Point(485, 320)); lgbTrees = new LinearGradientBrush(new Rectangle(420, 210, 80, 50), Color.LightGreen, Color.DarkGreen, LinearGradientMode.ForwardDiagonal); e.Graphics.FillEllipse(lgbTrees, new Rectangle(440, 200, 80, 40)); e.Graphics.DrawLine(new Pen(Color.Gray, 8), new Point(520, 230), new Point(520, 320)); lgbTrees = new LinearGradientBrush(new Rectangle(500, 190, 60, 100), Color.Aquamarine, Color.DarkGreen, LinearGradientMode.ForwardDiagonal); e.Graphics.FillEllipse(lgbTrees, new Rectangle(500, 180, 40, 120)); e.Graphics.DrawLine(new Pen(Color.Gray, 12), new Point(560, 250), new Point(560, 320)); lgbTrees = new LinearGradientBrush(new Rectangle(540, 240, 80, 50), Color.Lime, Color.DarkGreen, LinearGradientMode.ForwardDiagonal); e.Graphics.FillEllipse(lgbTrees, new Rectangle(520, 240, 80, 60)); } finally { Monitor.Exit(setup); } } } public static int Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Monitoring()); return 0; } }
This would produce:
Interlocking Some Threads
Introduction
While different threads are running in an application, they must coordinate their activities to eliminate or reduce conflicts with regards to atomicity. The interactions among threads sometimes occur when two or more of them are trying to access the same variable for any reason such as to change the value of the variable. To deal with atomic problems that could occur when different threads are trying to perform an operation on the same variable, the .NET Framework provides a static class named Interlocked.
Incrementing an Integer
One of the most routine operations performed in an application is to increment a number by 1, which is usually done in a for loop. If the value being incremented is globally defined, to make sure such an operation respects atomicity, the Interlocked provides an overloaded method named Increment. One of the versions is made for 32-bit integers. Its syntax is:
public static int Increment(ref int location)
The other version is for 64-bit integers. Its syntax is:
public static long Increment(ref long location);
The Interlocked.Increment() method takes a number as argument, adds 1 to it, and stores the result in the argument. When the method ends, the incrementing result can be retrived in the argument that is passed by reference. Here is an example of calling the 64-bit version of the method:
using System; using System.Drawing; using System.Threading; using System.Windows.Forms; public class Exercise : Form { public TextBox txtNumber; public TextBox txtResult; private Button btnIncrement; private Button btnClose; long number = 0L; object operation = new object(); object sequence = new object(); public Exercise() { InitializeComponent(); Application.EnableVisualStyles(); Application.Run(this); } private void InitializeComponent() { Controls.Add(new Label() { AutoSize = true, Location = new Point(12, 15), Text = "Number:" }); txtNumber = new TextBox() { Location = new Point(75, 12), Width = 75, Text = "0" }; Controls.Add(txtNumber); Controls.Add(new Label() { AutoSize = true, Location = new Point(12, 41), Text = "Result:" }); txtResult = new TextBox() { Location = new Point(75, 38), Width = 75, Text = "0" }; Controls.Add(txtResult); btnIncrement = new Button() { Location = new Point(155, 12), Width = 75, Text = "Increment" }; btnIncrement.Click += btnIncrementClick; Controls.Add(btnIncrement); btnClose = new Button() { Location = new Point(155, 38), Width = 75, Text = "Close" }; btnClose.Click += (sender, e) => { Close(); }; Controls.Add(btnClose); MaximizeBox = false; Text = "Elementary Algebra"; ClientSize = new System.Drawing.Size(250, 88); StartPosition = FormStartPosition.CenterScreen; } private void IncrementNumber() { // We will use some random numbers from the computer clock DateTime dtCurrent = DateTime.Now; Monitor.Enter(operation); // Lock the next section so that number1 would not be updated externally try { // Get some numbers from the system clock and perform operations on them. // Store the result in the first number number = dtCurrent.Year * dtCurrent.Millisecond + (dtCurrent.Month * dtCurrent.Second); // Display the number in the text box txtNumber.Text = number.ToString(); // Call the method that performs the necessar operation CalculateTotal(); // Make the application wait for a few seconds before the next operation Thread.Sleep(3000); } finally { Monitor.Exit(operation); } Monitor.Enter(sequence); try { // For the next operation, get the new seconds and milliseconds from the clock dtCurrent = DateTime.Now; // Perform some operations again. Add the new number to the previous number number += (number * 25) + dtCurrent.Year * dtCurrent.Second + (dtCurrent.Month * dtCurrent.Millisecond); // Display the new number in the text box txtNumber.Text = number.ToString(); CalculateTotal(); Thread.Sleep(2500); } finally { Monitor.Exit(sequence); } Monitor.Enter(operation); try { dtCurrent = DateTime.Now; number += dtCurrent.Second * 88; txtNumber.Text = number.ToString(); CalculateTotal(); } finally { Monitor.Exit(operation); } } private void CalculateTotal() { // Get the number from the text box number = long.Parse(txtNumber.Text); // Increment the number Interlocked.Increment(ref number); // Get the result from the argument that was passed by reference. // Display the number in the Result text box txtResult.Text = number.ToString(); } private void btnIncrementClick(object sender, EventArgs e) { Thread thIncrement = new Thread(new ThreadStart(IncrementNumber)); thIncrement.Start(); } [STAThread] public static int Main() { Exercise exo = new Exercise(); return 0; } }
Decrementing an Integer
The reverse of incrementing an integer is to subtract 1 from it. To support this operation in atomic scenarions, the Interlocked class is equipped with an overloaded method named Decrement. It provides a version form 32-bit integers and a version for 64-bit integers. Their syntaxes are:
public static int Decrement(ref int location) public static long Decrement(ref long location)
This method works like its Interlocked.Increment() counterpart, except that it subtract its argument by 1.
Adding Two Integers
One of the primary operations of the Interlocked class is to add two numbers. To assist you with this operation, the Interlocked class is equipped with an overloaded method named Add. It provides a version for 32-bit integers and a version for 64-bit integers. The syntaxes of this method are:
public static int Add(ref int location1, int value) public static long Add(ref long location1, long value)
This method adds its two arguments and stores the result in the first argument. This means that when the method ends, it returns the result in both the first argument and its own return value. Here is an example of calling the 32-bit version:
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
public class Exercise : Form
{
public TextBox txtNumber1;
public TextBox txtNumber2;
public TextBox txtResult;
private Button btnCaltulate;
private Button btnClose;
public Exercise()
{
InitializeComponent();
Application.EnableVisualStyles();
Application.Run(this);
}
private void InitializeComponent()
{
Controls.Add(new Label() { AutoSize = true, Location = new Point(12, 15), Text = "Number 1:" });
txtNumber1 = new TextBox() { Location = new Point(75, 12), Width = 75, Text = "0" };
Controls.Add(txtNumber1);
Controls.Add(new Label() { AutoSize = true, Location = new Point(12, 41), Text = "Number 2:" });
txtNumber2 = new TextBox() { Location = new Point(75, 38), Width = 75, Text = "0" };
Controls.Add(txtNumber2);
Controls.Add(new Label() { AutoSize = true, Location = new Point(12, 67), Text = "Result:" });
txtResult = new TextBox() { Location = new Point(75, 64), Width = 75, Text = "0" };
Controls.Add(txtResult);
btnCaltulate = new Button() { Location = new Point(155, 38), Width = 75, Text = "Calculate" };
btnCaltulate.Click += btnCaltulateClick;
Controls.Add(btnCaltulate);
btnClose = new Button() { Location = new Point(155, 64), Width = 75, Text = "Close" };
btnClose.Click+=btnCloseClick;
Controls.Add(btnClose);
MaximizeBox = false;
Text = "Elementary Algebra";
ClientSize = new System.Drawing.Size(250, 108);
StartPosition = FormStartPosition.CenterScreen;
}
private void btnCaltulateClick(object sender, EventArgs e)
{
int number1 = int.Parse(txtNumber1.Text);
int number2 = int.Parse(txtNumber2.Text);
Interlocked.Add(ref number1, number2);
txtResult.Text = number1.ToString();
}
private void btnCloseClick(object sender, EventArgs e)
{
Close();
}
[STAThread]
public static int Main()
{
Exercise exo = new Exercise();
return 0;
}
}
The Interlocked.Add() is very valuable if a thread needs to add numbers thast produce a lovery large integer. That's why the class provides a second version of the method for long integers. Its syntax is:
public static long Add(ref long location1, long value)
This works like the previous one except for long integers. Here is an example of calling it:
using System; using System.Drawing; using System.Threading; using System.Windows.Forms; public class Exercise : Form { public TextBox txtNumber1; public TextBox txtNumber2; public TextBox txtResult; private Button btnCaltulate; private Button btnClose; long number1 = 0L; long number2 = 0L; object first = new object(); object second = new object(); public Exercise() { InitializeComponent(); Application.EnableVisualStyles(); Application.Run(this); } private void InitializeComponent() { Controls.Add(new Label() { AutoSize = true, Location = new Point(12, 15), Text = "Number 1:" }); txtNumber1 = new TextBox() { Location = new Point(75, 12), Width = 75, Text = "0" }; Controls.Add(txtNumber1); Controls.Add(new Label() { AutoSize = true, Location = new Point(12, 41), Text = "Number 2:" }); txtNumber2 = new TextBox() { Location = new Point(75, 38), Width = 75, Text = "0" }; Controls.Add(txtNumber2); Controls.Add(new Label() { AutoSize = true, Location = new Point(12, 67), Text = "Result:" }); txtResult = new TextBox() { Location = new Point(75, 64), Width = 75, Text = "0" }; Controls.Add(txtResult); btnCaltulate = new Button() { Location = new Point(155, 38), Width = 75, Text = "Calculate" }; btnCaltulate.Click += btnCaltulateClick; Controls.Add(btnCaltulate); btnClose = new Button() { Location = new Point(155, 64), Width = 75, Text = "Close" }; btnClose.Click += btnCloseClick; Controls.Add(btnClose); MaximizeBox = false; Text = "Elementary Algebra"; ClientSize = new System.Drawing.Size(250, 108); StartPosition = FormStartPosition.CenterScreen; } private void GetFirstNumber() { // We will use some random numbers from the computer clock DateTime dtCurrent = DateTime.Now; Monitor.Enter(first); // Lock the next section so that number1 would not be updated externally try { // Get some numbers from the system clock and perform operations on them. // Store the result in the first number number1 = dtCurrent.Year * dtCurrent.Second + (dtCurrent.Month * dtCurrent.Millisecond); // Display the number in the first text box txtNumber1.Text = number1.ToString(); // Make the application wait for a few seconds before the next operation Thread.Sleep(2450); // Get the current numbers, add them, and display the result CalculateTotal(); } finally { Monitor.Exit(first); } Monitor.Enter(first); try { // For the next operation, get the new seconds and milliseconds from the clock dtCurrent = DateTime.Now; // Add the new number to the previous number number1 += (number1 * 25) + dtCurrent.Year * dtCurrent.Second + (dtCurrent.Month * dtCurrent.Millisecond); // Display the new number in the text box txtNumber1.Text = number1.ToString(); Thread.Sleep(2000); CalculateTotal(); } finally { Monitor.Exit(first); } Monitor.Enter(first); try { dtCurrent = DateTime.Now; Thread.Sleep(2000); number1 += dtCurrent.Second * 88; txtNumber1.Text = number1.ToString(); CalculateTotal(); } finally { Monitor.Exit(first); } } private void UupdateSecondValue() { DateTime dtCurrent = DateTime.Now; Monitor.Enter(second); // Lock the next section so that number2 would not be updated externally try { number2 = (dtCurrent.Second * dtCurrent.Millisecond) + (dtCurrent.Year + (dtCurrent.Hour * dtCurrent.Millisecond)); txtNumber2.Text = number2.ToString(); Thread.Sleep(2000); CalculateTotal(); Monitor.Enter(first); } finally { Monitor.Exit(second); } Monitor.Enter(second); try { dtCurrent = DateTime.Now; number2 += (number2 * 80) + (dtCurrent.Second * dtCurrent.Millisecond) + (dtCurrent.Year + (dtCurrent.Hour * dtCurrent.Millisecond)); txtNumber2.Text = number2.ToString(); CalculateTotal(); Monitor.Enter(first); } finally { Monitor.Exit(second); } Monitor.Enter(second); try { Thread.Sleep(2000); dtCurrent = DateTime.Now; number2 += dtCurrent.Millisecond * 7; txtNumber2.Text = number2.ToString(); Thread.Sleep(5000); CalculateTotal(); } finally { Monitor.Exit(second); } } private void CalculateTotal() { number1 = long.Parse(txtNumber1.Text); number2 = long.Parse(txtNumber2.Text); Interlocked.Add(ref number1, number2); txtResult.Text = number1.ToString(); } private void btnCaltulateClick(object sender, EventArgs e) { Thread thFirst = new Thread(new ThreadStart(GetFirstNumber)); Thread thOther = new Thread(UupdateSecondValue); thFirst.Start(); thOther.Start(); } private void btnCloseClick(object sender, EventArgs e) { Close(); } [STAThread] public static int Main() { Exercise exo = new Exercise(); return 0; } }
Exchanging Values
As you may be aware from your knowledge of C#, initializing a variable consists of assigning a value to it. In fact, assigning a value to a variable consists of giving a (new) value to it. In the same way, creating an object consists of assigning a reference from a class variable to it. You may also have to assign a list from one source to another list variable. The value, object, or list can be a constant or it can come from the return value of a called method, or from a list processed from some type of source. Sometimes this operation can be the subject of multiple operations from different threads, which means threads may interfere with each other when trying to initialize a variable or when trying to change/update a list.
To deal with atomic issues related to a variable, object, or list receiving (a) new value(s) or getting (an) object(s) for its object, the Interlocked class is equipped with an overloaded method named Exchange. It provides versions for integers (32-bit and 64-bit), floating-point numbers (single and double-precision), and objects of any kinds. The syntaxes of this method are:
public static int Exchange(ref int location1, int value, int comparand ) public static long Exchange(ref long location1, long value, long comparand ) public static float Exchange(ref float location1, float value, float comparand ) public static double Exchange(ref double location1, double value, double comparand ) public static object Exchange(ref object location1, object value, object comparand ) [ComVisibleAttribute(false)] public static T Exchange<T>(ref T location1, T value, T comparand ) where T : classpublic static IntPtr Exchange(ref IntPtr location1, IntPtr value, IntPtr comparand )
Here is an example of calling the double-precision number version of the method:
using System; using System.Drawing; using System.Threading; using System.Windows.Forms; public class Exercise : Form { public TextBox txtNumber; public TextBox txtResult; private Button btnCaltulate; private Button btnClose; public Exercise() { InitializeComponent(); Application.EnableVisualStyles(); Application.Run(this); } private void InitializeComponent() { Controls.Add(new Label() { AutoSize = true, Location = new Point(12, 15), Text = "Number:" }); txtNumber = new TextBox() { Location = new Point(75, 12), Width = 75, Text = "0.00" }; Controls.Add(txtNumber); Controls.Add(new Label() { AutoSize = true, Location = new Point(12, 41), Text = "Result:" }); txtResult = new TextBox() { Location = new Point(75, 38), Width = 75, Text = "0" }; Controls.Add(txtResult); btnCaltulate = new Button() { Location = new Point(155, 12), Width = 75, Text = "Calculate" }; btnCaltulate.Click += btnCaltulateClick; Controls.Add(btnCaltulate); btnClose = new Button() { Location = new Point(155, 38), Width = 75, Text = "Close" }; btnClose.Click += (sender, e) => { Close(); }; Controls.Add(btnClose); MaximizeBox = false; Text = "Elementary Algebra"; ClientSize = new System.Drawing.Size(250, 80); StartPosition = FormStartPosition.CenterScreen; } private void btnCaltulateClick(object sender, EventArgs e) { double result = 0.00D; double number = double.Parse(txtNumber.Text); Interlocked.Exchange(ref result, number); txtResult.Text = result.ToString(); } [STAThread] public static int Main() { Exercise exo = new Exercise(); return 0; } }
Comparing and Exchanging Two Values
Before performing an operation on a value that may be accessed by different threads, you can first compare them, or keep comparing them for equality and decide what to do. To assist you with this operation, the Interlocked class is equipped with an overloaded method named CompareExchange. It provides versions for integers (32-bit and 64-bit), floating-point numbers (single and double-precision), and objects of any kinds. The syntaxes of this method are:
public static int CompareExchange(ref int location1, int value, int comparand ) public static long CompareExchange(ref long location1, long value, long comparand ) public static float CompareExchange(ref float location1, float value, float comparand ) public static double CompareExchange(ref double location1, double value, double comparand ) public static object CompareExchange(ref object location1, object value, object comparand ) [ComVisibleAttribute(false)] public static T CompareExchange<T>(ref T location1, T value, T comparand ) where T : classpublic static IntPtr CompareExchange(ref IntPtr location1, IntPtr value, IntPtr comparand )
This method takes three arguments. The first argument is passed by reference. This means that you must initialize it before passing it to the method. The value of the first argument will be the return value of the method, regardless of what happens next. The other two arguments can have constant values or values from previously declared and initialized variables. The method compares the first and the second arguments. If they are equal, the first value of the second argument is stored in the first argument and that changes the first argument, which may change the return value.
|
||
Previous | Copyright © 2014-2021, FunctionX | Next |
|