Managing the Execution of a Thread

Passing an Object to a Thread

The Thread class has another constructor that allows you to pass values to a thread. Its syntax is:

public Thread(ParameterizedThreadStart start);

This constructor takes a ParameterizedThreadStart delegate that is a method that takes an object as argument:

public delegate void ParameterizedThreadStart(object obj);

Here is an example of using this constructor:

ParameterizedThreadStart tsLeftRightVehicle = new ParameterizedThreadStart(DrawLeftRightVehicle);
Thread thLeftRightVehicle = new Thread(tsLeftRightVehicle);

To actually let you pass the object to the thread, the Thread class has another version of the Start() method whose syntax is:

public void Start(object parameter);

This means that, when calling the Thread.Start() method, pass your object as argument. Here is an example:

namespace RoadTrafficMonitoring2
{
    public partial class TrafficSystem : Form
    {     
        private int xLeftRightVehicle, yLeftRightVehicle;
        private int xRightLeftVehicle, yRightLeftVehicle;

        private Image? imgBackground;
        private Image? imgLRVehicle, imgRLVehicle;

        public TrafficSystem()
        {
            InitializeComponent();
        }

        private void TrafficSystem_Load(object sender, EventArgs e)
        {
            imgBackground = Image.FromFile("C:\\Exercise1\\RoadMap1.png");
            imgLRVehicle  = Image.FromFile("E:\\functionx\\resources\\LRVehicle.png");
            imgRLVehicle  = Image.FromFile("E:\\functionx\\resources\\RLVehicle.png");

            Random rndNumber = new Random();

            xLeftRightVehicle = rndNumber.Next(0, pbxRoadMap.Width);
            yLeftRightVehicle = 226;
            xRightLeftVehicle = rndNumber.Next(0, pbxRoadMap.Width - 125);
            yRightLeftVehicle = 388;
        }

        private void pbxRoadMap_Paint(object sender, PaintEventArgs e)
        {
            Graphics grapher = e.Graphics;

            grapher.DrawImage(imgBackground!, 0, 0);
            grapher.DrawImage(imgLRVehicle!,  xLeftRightVehicle, yLeftRightVehicle);
            grapher.DrawImage(imgRLVehicle!,  xRightLeftVehicle, yRightLeftVehicle);
        }

        private record VehicleDetails
        {
            public string? Make  { get; set; }
            public string? Model { get; set; }
            public int     Year  { get; set; }
        }

        private void DrawLeftRightVehicle(object info)
        {
            if ((xLeftRightVehicle == 250) || (xLeftRightVehicle == 635) || (xLeftRightVehicle == 1019))
            {
                VehicleDetails? details = info as VehicleDetails;

                Text = "Road Traffic Monitoring - Stop: " +
                       details!.Make + " " + details.Model + " (" + details.Year.ToString() + ")";

                Thread.Sleep(5000);
            }
            else
                Text = "Road Traffic Monitoring";

            if (xLeftRightVehicle < pbxRoadMap.Width)
                xLeftRightVehicle++;
            else
                xLeftRightVehicle = -100;
        }

        private void DrawRightLeftVehicle()
        {
            if ((xRightLeftVehicle == 430) || (xRightLeftVehicle == 816) || (xRightLeftVehicle == 1199))
            {
                Thread.Sleep(4500);
            }

            if (xRightLeftVehicle < -100)
                xRightLeftVehicle = pbxRoadMap.Width;
            else
                xRightLeftVehicle--;
        }

        private void tmrDrawVehicles_Tick(object sender, EventArgs e)
        {
            VehicleDetails vLeftRight = new VehicleDetails();

            vLeftRight.Make  = "Ford";
            vLeftRight.Model = "Focus";
            vLeftRight.Year  = 2010;

            ParameterizedThreadStart tsLeftRightVehicle = new ParameterizedThreadStart(DrawLeftRightVehicle!);
            Thread      thLeftRightVehicle = new Thread(tsLeftRightVehicle);

            ThreadStart tsRightLeftVehicle = new ThreadStart(DrawRightLeftVehicle);
            Thread      thRightLeftVehicle = new Thread(tsRightLeftVehicle);

            thLeftRightVehicle.Start(vLeftRight);
            thRightLeftVehicle.Start();

            pbxRoadMap.Invalidate();
        }
    }
}

Practical LearningPractical Learning: Introducing Thread Execution

  1. Start Microsoft Visual Studion
  2. Create a new Windows Forms App named RoadTrafficMonitoring3
  3. In the Solution Explorer, right-click Form1.cs and click Rename
  4. Type TrafficSystem (to get TrafficSystem.cs)
  5. Press Enter twice
  6. Double-click the body of the form to generate its Load event
  7. Return to the form
  8. Using the images from the previous lesson for the vehicles, design the form as follows:

    Blocking a Thread - Form Design

    Controls (Name) Image SizeMode Other Properties
    PictureBox Picture Box pbxRoadMap RoadMap1.png   BorderStyle: Normal
    PictureBox Picture Box pbxLRVehicle LRVehicle.png AutoSize  
    PictureBox Picture Box pbxRLVehicle RLehicle.png AutoSize  
    PictureBox Picture Box TBVehicle1 TBVehicle1.png AutoSize  
    PictureBox Picture Box TBVehicle2 TBVehicle2.png AutoSize  
    PictureBox Picture Box TBVehicle3 TBVehicle3.png AutoSize  
    PictureBox Picture Box BTVehicle1 BTVehicle1.png AutoSize  
    PictureBox Picture Box BTVehicle2 BTVehicle2.png AutoSize  
    PictureBox Picture Box BTVehicle3 BTVehicle3.png AutoSize  
    Timer Timer tmrTrafficMonitoring     Enabled: True
    Interval: 20
  9. On the form, click the large picture box to select it
  10. In the Properties window, click the Events button
  11. In the Events section of the Properties window, double-click Paint
  12. Return to the form and, in its bottom section, double-click the tmrTrafficMonitoring

Blocking a Thread

In some cases, you may have one thread whose activities are interferring with the activities of another or other threads. One action you can take is to block the interfering thread to allow other threads to perform their assignments. To support this operation, the Thread class is equipped with an overloaded method named Join. The simplest version uses the following syntax:

public void Join();

This versions allows one thread to block another thread. The other two versions use the following syntaxes:

public bool Join(int millisecondsTimeout);
public bool Join(TimeSpan timeout);

Both versions take a number of seconds as argument. As a result, the thread that calls this method would be blocked for that amount of time.

Practical LearningPractical Learning: Blocking a Thread

  1. Change the document as follows:
    namespace RoadTrafficMonitoring3
    {
        public partial class TrafficSystem : Form
        {
            int     xLeftRightVehicle,  yLeftRightVehicle;
            int     xRightLeftVehicle,  yRightLeftVehicle;
            int     xTopBottomVehicle1, yTopBottomVehicle1;
            int     xTopBottomVehicle2, yTopBottomVehicle2;
            int     xTopBottomVehicle3, yTopBottomVehicle3;
            int     xBottomTopVehicle1, yBottomTopVehicle1;
            int     xBottomTopVehicle2, yBottomTopVehicle2;
            int     xBottomTopVehicle3, yBottomTopVehicle3;
    
            Thread? thLeftRightVehicle;
            Thread? thRightLeftVehicle;
            Thread? thTopBottomVehicle1;
            Thread? thTopBottomVehicle2;
            Thread? thTopBottomVehicle3;
            Thread? thBottomTopVehicle1;
            Thread? thBottomTopVehicle2;
            Thread? thBottomTopVehicle3;
    
            public TrafficSystem()
            {
                InitializeComponent();
            }
    
            private void TrafficSystem_Load(object sender, EventArgs e)
            {
                xLeftRightVehicle  =    0;
                yLeftRightVehicle  =  520;
                xRightLeftVehicle  = 1510;
                yRightLeftVehicle  =  475;
                xTopBottomVehicle1 =  376;
                yTopBottomVehicle1 =   38;
                xTopBottomVehicle2 =  778;
                yTopBottomVehicle2 =  154;
                xTopBottomVehicle3 = 1175;
                yTopBottomVehicle3 =  332;
                xBottomTopVehicle1 =  420;
                yBottomTopVehicle1 =  650;
                xBottomTopVehicle2 =  822;
                yBottomTopVehicle2 =  765;
                xBottomTopVehicle3 = 1222;
                yBottomTopVehicle3 =  965;
            }
    
            private void pbxRoadMap_Paint(object sender, PaintEventArgs e)
            {
                pbxLRVehicle.Location  = new Point(xLeftRightVehicle,  yLeftRightVehicle);
                pbxRLVehicle.Location  = new Point(xRightLeftVehicle,  yRightLeftVehicle);
                pbxTBVehicle1.Location = new Point(xTopBottomVehicle1, yTopBottomVehicle1);
                pbxTBVehicle2.Location = new Point(xTopBottomVehicle2, yTopBottomVehicle2);
                pbxTBVehicle3.Location = new Point(xTopBottomVehicle3, yTopBottomVehicle3);
                pbxBTVehicle1.Location = new Point(xBottomTopVehicle1, yBottomTopVehicle1);
                pbxBTVehicle2.Location = new Point(xBottomTopVehicle2, yBottomTopVehicle2);
                pbxBTVehicle3.Location = new Point(xBottomTopVehicle3, yBottomTopVehicle3);
            }
    
            private void DrawLeftRightVehicle()
            {
                /* If the car comes to a Stop sign, 
                 * it must stop/wait for 5 seconds before continuing. */
                if(xLeftRightVehicle == 266)
                    Thread.Sleep(3250);
                if(xLeftRightVehicle == 668)
                    Thread.Sleep(5000);
                if(xLeftRightVehicle == 1066)
                    Thread.Sleep(2500);
    
                if (xLeftRightVehicle < 1515)
                    xLeftRightVehicle += 2;
                else
                    xLeftRightVehicle = 0;
            }
    
            private void DrawRightLeftVehicle()
            {
                // If the car comes to a Stop sign, it must
                // stop/wait for 5 seconds before continuing
                TimeSpan waitTime = new TimeSpan(0, 0, 5);
    
                if(xRightLeftVehicle == 465)
                    Thread.Sleep(waitTime);
                if(xRightLeftVehicle == 870)
                    Thread.Sleep(waitTime);
                if(xRightLeftVehicle == 1270)
                    Thread.Sleep(waitTime);
    
                if (xRightLeftVehicle < 0)
                    xRightLeftVehicle = 1510;
                else
                    xRightLeftVehicle--;
            }
    
            private void DrawTopBottomVehicle1()
            {
                /* If the car comes to a Stop sign, 
                 * it must stop/wait for 7.5 seconds before continuing. */
                if (yTopBottomVehicle1 == 390)
                    thTopBottomVehicle1!.Join(7500);
    
                if (yTopBottomVehicle1 < 975)
                    yTopBottomVehicle1++;
                else
                    yTopBottomVehicle1 = 30;
            }
    
            private void DrawTopBottomVehicle2()
            {
                if (yTopBottomVehicle2 == 395)
                    thTopBottomVehicle2!.Join(4200);
    
                if (yTopBottomVehicle2 < 978)
                    yTopBottomVehicle2++;
                else
                    yTopBottomVehicle2 = 30;
            }
    
            private void DrawTopBottomVehicle3()
            {
                if (yTopBottomVehicle3 == 390)
                    thTopBottomVehicle3!.Join(5200);
    
                if (yTopBottomVehicle3 < 975)
                    yTopBottomVehicle3 += 2;
                else
                    yTopBottomVehicle3 = 30;
            }
    
            private void DrawBottomTopVehicle1()
            {
                TimeSpan waitTime = new TimeSpan(0, 0, 5);
    
                if (yBottomTopVehicle1 == 566)
                    thBottomTopVehicle1!.Join(waitTime);
    
                if (yBottomTopVehicle1 < 30)
                    yBottomTopVehicle1 = 978;
                else
                    yBottomTopVehicle1 -= 2;
            }
    
            private void DrawBottomTopVehicle2()
            {
                TimeSpan waitTime = new TimeSpan(0, 0, 12);
    
                if ((yBottomTopVehicle2 == 564) || (yBottomTopVehicle2 == 566))
                    thBottomTopVehicle2!.Join(waitTime);
    
                if (yBottomTopVehicle2 < 30)
                    yBottomTopVehicle2 = 978;
                else
                    yBottomTopVehicle2 -= 3;
            }
    
            private void DrawBottomTopVehicle3()
            {
                TimeSpan waitTime = new TimeSpan(0, 0, 8);
    
                if (yBottomTopVehicle3 == 566)
                    thBottomTopVehicle3!.Join(waitTime);
    
                if (yBottomTopVehicle3 < 30)
                    yBottomTopVehicle3 = 978;
                else
                    yBottomTopVehicle3--;
            }
    
            private void tmrTrafficMonitoring_Tick(object sender, EventArgs e)
            {
                ThreadStart tsLeftRightVehicle = new ThreadStart(DrawLeftRightVehicle);
                thLeftRightVehicle = new Thread(tsLeftRightVehicle);
    
                ThreadStart tsRightLeftVehicle = new ThreadStart(DrawRightLeftVehicle);
                thRightLeftVehicle = new Thread(tsRightLeftVehicle);
    
                thTopBottomVehicle1 = new Thread(new ThreadStart(DrawTopBottomVehicle1));
                thTopBottomVehicle2 = new Thread(new ThreadStart(DrawTopBottomVehicle2));
                thTopBottomVehicle3 = new Thread(new ThreadStart(DrawTopBottomVehicle3));
                thBottomTopVehicle1 = new Thread(new ThreadStart(DrawBottomTopVehicle1));
                thBottomTopVehicle2 = new Thread(new ThreadStart(DrawBottomTopVehicle2));
                thBottomTopVehicle3 = new Thread(new ThreadStart(DrawBottomTopVehicle3));
    
                thLeftRightVehicle.Start();
                thRightLeftVehicle.Start();
                thTopBottomVehicle1.Start();
                thTopBottomVehicle2.Start();
                thTopBottomVehicle3.Start();
                thBottomTopVehicle1.Start();
                thBottomTopVehicle2.Start();
                thBottomTopVehicle3.Start();
    
                pbxRoadMap.Invalidate();
            }
        }
    }
  2. To execute, press Ctrl + F5

    Blocking a Thread - Form Design

    Blocking a Thread - Form Design

    Blocking a Thread - Form Design

  3. Close the form and return to Microsoft Visual Studio

Threading and Object-Oriented Programming

Object-oriented programming is the ability to programmatically create and describe objects. One or more objects may be used. Whether it is one or more, a common set of characteristics is used to describe each object. Those common characteristics are grouped in a class (or structure). The characteristics of a class are called properties (and/or fields). For example, a moving vehicle can be represented by such characteristics as its location, which can be represented by a point (Point or PointF structure) or x and y coordinates. Other details could include the picture of the car and/or its speed, which determines how fast (or slow) the vehicle is moving. While the characteristics describe an object, actions are what an object can do, such as moving or drawing itself.

Once the class or structure has been created, variables can be declared from it, and then the members of the class can be accessed.

Practical LearningPractical Learning: Creating a Threading Object

  1. Start a new Windows Forms App named RoadTrafficMonitoring4
  2. In the Solution Explorer, right-click the name of the Project -> Add -> New Folder
  3. Type Models and press Enter
  4. In the Solution Explorer, right-click Models -> Add -> Class...
  5. Type Car and press Enter
  6. Change the document as follows:
    namespace RoadTrafficMonitoring4.Models
    {
        internal interface IVehicle
        {
            int X { get; set; }
            int Y { get; set; }
            PictureBox? Picture { get; set; }
            void Draw();
        }
    
        internal record Car : IVehicle
        {
            public int X { get; set; }
            public int Y { get; set; }
            public PictureBox? Picture { get; set; }
    
            public Car(int x, int y, PictureBox picture)
            {
                X = x;
                Y = y;
                Picture = picture;
            }
    
            public void Draw()
            {
                Picture!.Location = new Point(X, Y);
            }
        }
    }
  7. In the Solution Explorer, right-click Form1.cs and click Rename
  8. Type TrafficSystem (to get TrafficSystem.cs)
  9. Press Enter twice (once to indicate that you want to rename the form, second to access the form)
  10. Double-click the body of the form to generate its Load event
  11. Return to the form
  12. Using the images from the previous lesson, design the form as follows:

    Blocking a Thread - Form Design

    Controls (Name) Image SizeMode Other Properties
    PictureBox Picture Box pbxRoadMap RoadMap1.jpg   BorderStyle: Normal
    PictureBox Picture Box pbxLRVehicle LRVehicle.png AutoSize  
    PictureBox Picture Box pbxRLVehicle RLehicle.png AutoSize  
    PictureBox Picture Box TBVehicle1 TBVehicle1.png AutoSize  
    PictureBox Picture Box TBVehicle2 TBVehicle2.png AutoSize  
    PictureBox Picture Box TBVehicle3 TBVehicle3.png AutoSize  
    PictureBox Picture Box BTVehicle1 BTVehicle1.png AutoSize  
    PictureBox Picture Box BTVehicle2 BTVehicle2.png AutoSize  
    PictureBox Picture Box BTVehicle3 BTVehicle3.png AutoSize  
    Timer Timer tmrTrafficMonitoring     Enabled: True
    Interval: 20
  13. Click the body of the form to select the large picture box
  14. In the Properties window, click the Events button Events
  15. In the Events section of the Properties window, double-click Paint
  16. Return to the form and, in its bottom section, double-click the tmrTrafficMonitoring
  17. Change the document as follows:
    using RoadTrafficMonitoring4.Models;
    
    namespace RoadTrafficMonitoring4
    {
        public partial class TrafficSystem : Form
        {
            IVehicle? LeftRight, RightLeft;
            IVehicle? TopBottom1, TopBottom2, TopBottom3;
            IVehicle? BottomTop1, BottomTop2, BottomTop3;
    
            static Thread? thLeftRightVehicle = null;
            static Thread? thRightLeftVehicle = null;
            static Thread? thTopBottomVehicle1 = null;
            static Thread? thTopBottomVehicle2 = null;
            static Thread? thTopBottomVehicle3 = null;
            static Thread? thBottomTopVehicle1 = null;
            static Thread? thBottomTopVehicle2 = null;
            static Thread? thBottomTopVehicle3 = null;
    
            public TrafficSystem()
            {
                InitializeComponent();
            }
    
            private void TrafficSystem_Load(object sender, EventArgs e)
            {
                Random rndNumber = new Random();
    
                LeftRight  = new Car(rndNumber.Next(0, pbxRoadMap.Width),       pbxLRVehicle.Top, pbxLRVehicle);
                RightLeft  = new Car(rndNumber.Next(0, pbxRoadMap.Width - 125), pbxRLVehicle.Top, pbxRLVehicle);
    
                TopBottom1 = new Car(pbxTBVehicle1.Left, rndNumber.Next(0, pbxRoadMap.Height),   pbxTBVehicle1);
                TopBottom2 = new Car(pbxTBVehicle2.Left, rndNumber.Next(0, pbxRoadMap.Height),   pbxTBVehicle2);
                TopBottom3 = new Car(pbxTBVehicle3.Left, rndNumber.Next(0, pbxRoadMap.Height),   pbxTBVehicle3);
                BottomTop1 = new Car(pbxBTVehicle1.Left, rndNumber.Next(pbxRoadMap.Height - 95), pbxBTVehicle1);
                BottomTop2 = new Car(pbxBTVehicle2.Left, rndNumber.Next(pbxRoadMap.Height - 88), pbxBTVehicle2);
                BottomTop3 = new Car(pbxBTVehicle3.Left, rndNumber.Next(pbxRoadMap.Height - 90), pbxBTVehicle3);
            }
    
            private void pbxRoadMap_Paint(object sender, PaintEventArgs e)
            {
                LeftRight!.Draw();
                RightLeft!.Draw();
                TopBottom1!.Draw();
                TopBottom2!.Draw();
                TopBottom3!.Draw();
                BottomTop1!.Draw();
                BottomTop2!.Draw();
                BottomTop3!.Draw();
            }
    
            void DrawLeftRightVehicle()
            {
                if ((LeftRight!.X == 264) || (LeftRight.X == 665) || (LeftRight.X == 1066))
                    Thread.Sleep(5000);
    
                if (LeftRight.X < pbxRoadMap.Width)
                    LeftRight.X++;
                else
                    LeftRight.X = -100;
            }
    
            void DrawRightLeftVehicle()
            {
                if ((RightLeft!.X == 465) || (RightLeft.X == 870) || (RightLeft.X == 1265))
                    Thread.Sleep(4500);
    
                if (RightLeft.X < -100)
                    RightLeft.X = pbxRoadMap.Width;
                else
                    RightLeft.X--;
            }
    
            void DrawTopBottomVehicle1()
            {
                if (TopBottom1!.Y == 390)
                    thTopBottomVehicle1!.Join(5000);
    
                if (TopBottom1.Y < pbxRoadMap.Height)
                    TopBottom1.Y++;
                else
                    TopBottom1.Y = -100;
            }
    
            void DrawTopBottomVehicle2()
            {
                if (TopBottom2!.Y == 395)
                    thTopBottomVehicle2!.Join(4200);
    
                if (TopBottom2.Y < pbxRoadMap.Height)
                    TopBottom2.Y++;
                else
                    TopBottom2.Y = -100;
            }
    
            void DrawTopBottomVehicle3()
            {
                if (TopBottom3!.Y == 382)
                    thTopBottomVehicle3!.Join(3200);
    
                if (TopBottom3.Y < pbxRoadMap.Height)
                    TopBottom3.Y += 2;
                else
                    TopBottom3.Y = -100;
            }
    
            void DrawBottomTopVehicle1()
            {
                if ((BottomTop1!.Y >= 567) && (BottomTop1.Y <= 569))
                    thBottomTopVehicle1!.Join(4600);
    
                if (BottomTop1.Y < -100)
                    BottomTop1.Y = pbxRoadMap.Height;
                else
                    BottomTop1.Y -= 2;
            }
    
            void DrawBottomTopVehicle2()
            {
                if(BottomTop2!.Y == 568)
                    thBottomTopVehicle2!.Join(3450);
    
                if (BottomTop2.Y < -100)
                    BottomTop2.Y = pbxRoadMap.Height;
                else
                    BottomTop2.Y -= 1;
            }
    
            void DrawBottomTopVehicle3()
            {
                if (BottomTop3!.Y == 565)
                    thBottomTopVehicle3!.Join(4950);
    
                if (BottomTop3.Y < -100)
                    BottomTop3.Y = pbxRoadMap.Height;
                else
                    BottomTop3.Y--;
            }
    
            private void tmrTrafficMonitoring_Tick(object sender, EventArgs e)
            {
                ThreadStart tsLeftRightVehicle = new ThreadStart(DrawLeftRightVehicle);
                thLeftRightVehicle             = new Thread(tsLeftRightVehicle);
    
                ThreadStart tsRightLeftVehicle = new ThreadStart(DrawRightLeftVehicle);
                thRightLeftVehicle             = new Thread(tsRightLeftVehicle);
    
                thTopBottomVehicle1 = new Thread(new ThreadStart(DrawTopBottomVehicle1));
                thTopBottomVehicle2 = new Thread(new ThreadStart(DrawTopBottomVehicle2));
                thTopBottomVehicle3 = new Thread(new ThreadStart(DrawTopBottomVehicle3));
                thBottomTopVehicle1 = new Thread(new ThreadStart(DrawBottomTopVehicle1));
                thBottomTopVehicle2 = new Thread(new ThreadStart(DrawBottomTopVehicle2));
                thBottomTopVehicle3 = new Thread(new ThreadStart(DrawBottomTopVehicle3));
    
                thLeftRightVehicle.Start();
                thRightLeftVehicle.Start();
                thTopBottomVehicle1.Start();
                thTopBottomVehicle2.Start();
                thTopBottomVehicle3.Start();
                thBottomTopVehicle1.Start();
                thBottomTopVehicle2.Start();
                thBottomTopVehicle3.Start();
    
                pbxRoadMap.Invalidate();
            }
        }
    }
  18. To execute the project, on the main menu of Microsoft Visual Studio, click Debug -> Start Without Debugging
  19. Close the form and return to Microsoft Visual Studio

Exceptionally Handling a Thread

Introduction

A thread is subject to many problems, including internal interruptions (one thread in an application may want to stop another thread of the same application from doing its work) and external conflicts (threads from different applications may want to access, and therefore compete for, the same resource). To deal with these and other types of problems, you should handle exceptions in the threads of your application.

As you should know from your knowledge of C#, the normal flow of code is handled in a try block. The errors or exceptions are handled in a catch block. The formula to follow is:

try {
    // Normal thread operation(s)
}
catch(argument)
{
    // Handling exception(s)
}

Of course, you can include as many catch blocks as you judge necessary.

Aggregating the Execution of an Application

As mentioned in our introduction, a thread is a small program that contributes to the execution of a process (also called an application). Something inside the thread may cause it to behave badly. For example, an internal calculation may go wrong. One thread may interfere, or try to interfere, with the job that another thread is trying to accomplish. These types of problems may interrupt a process or make the result of an application unpredictable. To assist you with these types of problems, the .NET Framework provides a class named AggregateException. This class includes many constructors with different goals.

The primary way to use the AggregateException exception is to pass it to the catch() clause. Here is an example:

namespace RoadTrafficMonitoring21
{
    public partial class Exercise : Form
    {
        int xPosition;
        int yPosition;

        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            Random rndNumber = new Random();

            xPosition = rndNumber.Next(0, 900);
            yPosition = 125;
        }

        void MoveVehicle()
        {
            if (xPosition < pbxRoadMap.Width)
                xPosition++;
            else
                xPosition = -100;
        }

        private void pbxRoadMap_Paint(object sender, PaintEventArgs e)
        {
            pbxLRVehicle.Location = new Point(xPosition, yPosition);
        }

        private void tmrDrawVehicles_Tick(object sender, EventArgs e)
        {
            ThreadStart ts = new ThreadStart(MoveVehicle);
            Thread thVehicle = new Thread(ts);

            try
            {
                thVehicle.Start();
                pbxRoadMap.Invalidate();
            }
            catch (AggregateException ae)
            {
                MessageBox.Show("Something went wrong and interferred with the application's " +
                                "execution. Please report the error as follows." + Environment.NewLine +
                                ae.Message, "Traffic Monitoring",
                                MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
    }
}}

One of the constructors of the AggregateException class allows you to specify its message by passing a string to the constructor. Its syntax is:

public AggregateException(string message)

After initializing an AggregateException object with this constructor, its argument becomes the message of the exception. Here is an example of using this constructor:

namespace RoadTrafficMonitoring21
{
    public partial class Exercise : Form
    {
        int xPosition;
        int yPosition;

        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            Random rndNumber = new Random();

            xPosition = rndNumber.Next(0, 900);
            yPosition = 125;
        }

        void MoveVehicle()
        {
            if (xPosition < pbxRoadMap.Width)
                xPosition++;
            else
                xPosition = -100;
        }

        private void pbxRoadMap_Paint(object sender, PaintEventArgs e)
        {
            pbxLRVehicle.Location = new Point(xPosition, yPosition);
        }

        private void tmrDrawVehicles_Tick(object sender, EventArgs e)
        {
            ThreadStart ts = new ThreadStart(MoveVehicle);
            Thread thVehicle = new Thread(ts);

            try
            {
                thVehicle.Start();
                pbxRoadMap.Invalidate();
            }
            catch (AggregateException ae)
            {
                ae = new AggregateException("Something went wrong and interferred with the application's execution.");

                MessageBox.Show("Something went wrong and interferred with the application's " +
                                "execution. Please report the error as follows." + Environment.NewLine +
                                ae.Message, "Traffic Monitoring",
                                MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
    }
}

An Inner Exception

During its lifetime, a thread can throw many exceptions and exceptions from other threads can affect it. Problems or exceptions can involve threads from the same application as well as threads from other applications. Exceptions can also be caused by (child) threads created inside of (parent) threads. An inner exception is an exception that causes another exception. To support inner exceptions, the AggregateException class inherits a property named InnerException from the Exception class.

A Collection of Exceptions

As one exception can cause another exception, many exceptions can cause an exception. The various exceptions that can cause an exception are treated as a collection. To let you get the collection of exceptions that have caused an exception, the AggregateException class includes a collection-based property named InnerExceptions:

public ReadOnlyCollection<Exception> InnerExceptions { get; }

To access each inner exception that caused an exception, you can use a foreach loop that will visit each item of the AggregateException object. To help you manage the exceptions that cause an exception, the AggregateException class includes various constructors that can be used to initialize an AggregateException object with a collection of exceptions related to its thread.

Handling Each Inner Exception

To assist you in handling an exception that is in the collection of the AggregateException exceptions, the AggregateException class includes a Boolean method named Handle. Its syntax is:

public void Handle(Func<Exception, bool> predicate)

This method takes an Exception object as argument and the function. The function returns a Boolean value that indicates whether the exception was actually handled. Here is an example of calling this method:

namespace RoadTrafficMonitoring21
{
    public partial class Exercise : Form
    {
        int xPosition;
        int yPosition;

        public Exercise()
        {
            InitializeComponent();
        }

        private void Exercise_Load(object sender, EventArgs e)
        {
            Random rndNumber = new Random();

            xPosition = rndNumber.Next(0, 900);
            yPosition = 125;
        }

        void MoveVehicle()
        {
            if (xPosition < pbxRoadMap.Width)
                xPosition++;
            else
                xPosition = -100;
        }

        bool PresentError(Exception e)
        {
            MessageBox.Show("The current error occurred as follows: " + e.Message);

            return true;
        }

        private void pbxRoadMap_Paint(object sender, PaintEventArgs e)
        {
            pbxLRVehicle.Location = new Point(xPosition, yPosition);
        }

        private void tmrDrawVehicles_Tick(object sender, EventArgs e)
        {
            ThreadStart ts = new ThreadStart(MoveVehicle);
            Thread thVehicle = new Thread(ts);

            try
            {
                thVehicle.Start();
                pbxRoadMap.Invalidate();
            }
            catch (AggregateException ae)
            {
                ae.Handle(PresentError);

                MessageBox.Show("Something went wrong and interferred with the application's " +
                                "execution. Please report the error as follows." + Environment.NewLine +
                                ae.Message, "Traffic Monitoring",
                                MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
    }
}

Of course, you don't have to first define the function. You can implement it directly where it is needed.

When a Thread Aborts

As we saw in the previous lesson, one way to request that a thread be interrupted is to call the Thread.Abort() method. When this method is called, the operating system (OS) is asked to decide whether to terminate the operations of a thread. At that time, the OS throws an exception named ThreadAbortException. This class has only one member as a property named ExceptionState that is of type object:

public object ExceptionState { get; }

This property actually holds the information you should have passed to the Thread.Abort() method. Remember that the Thread.Abort() method doesn't actually terminate a thread. Therefore, neither call the Thread.Abort() method nor handle a ThreadAbortException exception in code that is continually executing, such as code that runs in a timer.

Practical LearningPractical Learning: Ending the Lesson


Previous Copyright © 2014-2024, FunctionX Tuesday 02 July 2024, 10:55 Next