Executing a Thread
Executing a Thread
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 Learning: Introducing Thread Execution
Controls | (Name) | Image | SizeMode | Other Properties | |
PictureBox | pbxRoadMap | RoadMap1.png | BorderStyle: Normal | ||
PictureBox | pbxLRVehicle | LRVehicle.png | AutoSize | ||
PictureBox | pbxRLVehicle | RLehicle.png | AutoSize | ||
PictureBox | TBVehicle1 | TBVehicle1.png | AutoSize | ||
PictureBox | TBVehicle2 | TBVehicle2.png | AutoSize | ||
PictureBox | TBVehicle3 | TBVehicle3.png | AutoSize | ||
PictureBox | BTVehicle1 | BTVehicle1.png | AutoSize | ||
PictureBox | BTVehicle2 | BTVehicle2.png | AutoSize | ||
PictureBox | BTVehicle3 | BTVehicle3.png | AutoSize | ||
Timer | tmrTrafficMonitoring | Enabled: True Interval: 20 |
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 Learning: Blocking a Thread
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(); } } }
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 Learning: Creating a Threading Object
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); } } }
Controls | (Name) | Image | SizeMode | Other Properties | |
PictureBox | pbxRoadMap | RoadMap1.jpg | BorderStyle: Normal | ||
PictureBox | pbxLRVehicle | LRVehicle.png | AutoSize | ||
PictureBox | pbxRLVehicle | RLehicle.png | AutoSize | ||
PictureBox | TBVehicle1 | TBVehicle1.png | AutoSize | ||
PictureBox | TBVehicle2 | TBVehicle2.png | AutoSize | ||
PictureBox | TBVehicle3 | TBVehicle3.png | AutoSize | ||
PictureBox | BTVehicle1 | BTVehicle1.png | AutoSize | ||
PictureBox | BTVehicle2 | BTVehicle2.png | AutoSize | ||
PictureBox | BTVehicle3 | BTVehicle3.png | AutoSize | ||
Timer | tmrTrafficMonitoring | Enabled: True Interval: 20 |
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(); } } }
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 Learning: Ending the Lesson
Previous | Copyright © 2014-2024, FunctionX | Tuesday 02 July 2024, 10:55 | Next |