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;
    }
}

Finding an Item in an Array

Finding an Item in an Array

Finding an Item in an Array

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;
    }
}

Introduction to Atomicity

Introduction to Atomicity

Introduction to Atomicity

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;
    }
}

Introduction to Atomicity

Introduction to Atomicity

Introduction to Atomicity

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:

Synchronous and Asynchronous Operations

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:

Synchronous and Asynchronous Operations

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;
    }
}

Incrementing an Integer

Incrementing an Integer

Incrementing an Integer

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;
    }
}

Primary Threads Interactions Operations

Primary Threads Interactions Operations

Primary Threads Interactions Operations

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;
    }
}

Primary Threads Interactions Operations

Primary Threads Interactions Operations

Primary Threads Interactions Operations

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