Foundations of Threading

Introduction to Processes

The primary purpose of a computer is to provide electronic tools that allow a person to perform various types of operations, fast. This is made possible because the computer contains various types of applications, also called computer applications, or computer programs.

A computer application is made of one or various sub-applications that perform relatively small operations. The main purpose of a program is to do something, such as performing calculations in a spreadsheet or storing values in the computer memory. To make this possible, the operating system must be able to launch or execute a program. This involves millions (or billions) of operations performed and processed behind-the-scenes in the computer. For this reason, a computer application is called a process.

Creating a Process

The Win32 library is equipped with various types of functions to create and manage processes. To support processes, the .NET Framework provides a class named Process that is defined in the System.Diagnostics namespace. To give you more information about a process, the .NET Framework provides a class named ProcessStartInfo, also defined in the System.Diagnostics namespace.

Introduction to Threads

As mentioned previously, a process or application is made of small sub-programs that take care of internal operations of a computer application. For example, in a graphics application, one sub-program can be in charge of arranging a series of bits (1s and 0s) to produce a clear or recognizable picture. Another sub-program can check the correctness of bits and retrieve their particular arrangement (of the bits) to display them on the monitor screen to produce a meaningful picture. Another sub-program can take care of keeping the arranged bits on the screen so the picture or graphics would not randomly vibrate, and so on. Another sub-program may be in charge of displaying an application on the screen, to the user. Those sub-programs are put (or work) together to produce the results sought by a user. Of courses, these sub-programs work behind-the-scenes (and of course, the user doesn't care about what is happening). These small sub-applications are called threads.

For a process to perform its desired operation(s), the operating system must give it (allocate to it) the resources that are necessary. There are low-level resources that involve the hardware and high-level resources. Examples of low-level resources are the processor's ability to perform calculations, the processor's time used to perform a calculation or concentrate on some job a process needs, the processor's ability to consider that a certain job is more important (or requires priority) than (as compared to) another job, the motherboard's ability to carry (transport) information (data) from one side of the computer to another, a (hard, optical, USB, etc) drive's ability to read or write 1s and 0s, etc. Examples of high-level resources are the name of the application that is using the resources, the folder or the directory in which the application is installed, the permissions used to deal with the process, the ability to start or stop a process.

Obviously, this simplistic description would involve millions or billions of operations. A thread is something, anything, that needs and receives the operating system's attention to perform a particular operation. As a result, a process is in fact one or a series of threads that perform the necessary operations.

Single-Tasking Environment

In a single-tasking environment, one operation is performed. When that operation ends, the next operation starts and in fact, one operation must wait for the ongoing (or previous) operation to complete.

Multi-Tasking Environment

When computers were performing a limited number of operations and processors were simple, the operating system was solely in charge of dealing with threads. Nowadays, computers and processors have become complex and we have become demanding with them. In fact, a typical computer or user nowadays needs to do many things at the same time, in what we have designated as multitasking.

Modern processors are now equipped with many internal sub-processors, called cores, that can internally, but also independently, and simultaneously, perform many individual operations. As a result, you can create such individual operations for your application so that many individual operations can be performed at the same time instead of waiting for the ongoing (or the previous) operation to end.

Creating a Thread

Introduction

Multi-tasking programming consists of deciding how threads should manage your application. You start by creating one or more threads in your application and specifying what they should do. To support the ability to create threads, the .NET Framework provides a sealed class named Thread:

public sealed class Thread : CriticalFinalizerObject, 
			                 _Thread

A thread created in the .NET Framework, using the Thread class, is referred to as managed. This class is defined in the System.Threading namespace. The easiest way to create a thread is to declare a variable of this class. To let you initialize the variable, the Thread class is equipped with four constructors and there is no default constructor. The easiest constructor takes a delegate argument that specifies the method that will carry the operation of the thread. Its syntax is:

public Thread(ThreadStart start);

This constructor takes an argument based on a delegate named ThreadStart:

public delegate void ThreadStart();

This means that the method associated with the delegate takes no argument and returns nothing. This also means that you must have a method, and you have various options:

For the first three options, we created a regular method. As an option, you can create the method as static. If you do this, make sure you follow all the rules of using a static method.

Creating Many Threads

As opposed to one, you may need to have various threads on your webpage. You can create each thread and use it as you see fit.

Primary Operations on a Thread

The Apartment Area of a Thread

When an application starts, or to manage a running program, the operating system allocates the necessary resources for it, such as the appropriate amount of memory. To manage the resources used by the threads of an application, the operating system also reserves an area, called an apartment, that those threads can consult for available resources. The characteristics of an apartment are defined in an enumeration named ApartmentState:

public enum ApartmentState { MTA, STA, Unknown };

In reality, to let you specify how an application (a process) will manage the apartment, the .NET Framework provides two attributes. Meanwhile, the members of the ApartmentState enumeration are:

To assist you with management or inquiries related to an apartment, the Thread class is equipped with methods named GetApartmentState() and the SetApartmentState(). Their syntaxes are:

public ApartmentState GetApartmentState();
public void SetApartmentState(ApartmentState state);

As you can see, the combination of these two methods results in a (an undefined) property.

Starting a Thread

Obviously you are not the only one creating threads, or your application is not the only one running in the computer. As a result, when you create a thread, you are simply informing the operating system that you will use a thread, but you must specify when to execute that thread. To support this operation, the Thread class is equipped with an overloaded method named Start. The simplest version uses the following syntax:

public void Start();

As you can see, this is a simple method. It takes no argument and returns nothing. Here is an example of calling it:

using System;
using System.Web;
using System.Web.Mvc;
using System.Threading;

namespace Exercises.Controllers
{
    public class StudiesController : Controller
    {
        // GET: Studies
        public ActionResult Index()
        {
            ThreadStart tsMove = new ThreadStart(WalkHome);

            Thread thPerson = new Thread(tsMove);

            thPerson.Start();

            return View();
        }

        public void WalkHome()
        {
            Response.Write("<p>I learned long ago, never to wrestle with a pig. You get dirty, and besides, the pig likes it. - George Bernard Shaw -</p>");
        }
    }
}

In the same way, if you had created many threads, you can execute (or start) each when you want.

Suspending a Thread

For some reason (and it will be up to you), you will not always want your thread to run eternally. At some point, you may want to ask it to wait some time and resume later. This operation is referred to as pausing or suspending the activities of a thread. Once again, you have various options. To support the ability to pause a thread, the Thread class is equipped with an overloaded static method named Sleep. One of the versions uses the following syntax:

public static void Sleep(int millisecondsTimeout);

With this version, you pass the number of milliseconds during which the thread must wait before resuming its activities. Here is an example of calling this method:

using System.Web.Mvc;
using System.Threading;

namespace Exercises.Controllers
{
    public class StudiesController : Controller
    {
        // GET: Studies
        public ActionResult Index()
        {
            ThreadStart tsMove = new ThreadStart(WalkHome);

            Thread thPerson = new Thread(tsMove);

            Thread.Sleep(5000);
            
            thPerson.Start();

            return View();
        }

        public void WalkHome()
        {
            Response.Write("<p>I learned long ago, never to wrestle with a pig. You get dirty, and besides, the pig likes it. - George Bernard Shaw -</p>");
        }
    }
}

The other version of the Thread.Sleep() method takes a TimeSpan argument. Its syntax is:

public static void Sleep(TimeSpan timeout);

A Thread in Infinite Timeout

If you had created many threads, when necessary, you can suspend those you want when you want. The let you suspend a thread indefinitely, the .NET Framework provides the very short static Timeout class that is equipped with only two constant-value fields as members. They are named Infinite and InifiniteTimeSpan:

public const int Infinite;
public static readonly TimeSpan InfiniteTimeSpan;

Based on this, to indefinitely put a thread to sleep, pass Timeout.Infinite as argument to the Thread.Sleep() method.

Terminating a Thread

As mentioned already, a thread is part of an application. This means that when an application closes, the operating system (OS) terminates all the related or associated threads. This also means that normally, the OS takes (and is equipped to take) care of threads. Still, in some cases (and only you will decide why), you will need to terminate a thread, even if its application is still running.

As one way to support the ability to terminate a thread, the Thread class is equipped with an overloaded method named Abort. Actually, the Thread.Abort() method neither suspends nor terminates a thread. It only let the OS know that the thread should be terminated. The operating system is left with deciding what to do (including when and how).

One of the versions of the Thread.Abort() method takes no argument. Its syntax is:

public void Abort();

As you can see, this method is simply asked to terminate its thread (remember that this method doesn't really terminate a thread). In the same way, you can terminate as many threads as you want in your program.

The Thread.Abort() method hardly does any processing. In fact, this method relies on you (and on the operating system or OS) to take care of any problem. Thread abortion (or termination) is neither guaranteed nor predictable. One way you can assist, or inform, the OS about the operation (of terminating your thread) is to provide some information about it. To let you provide such information, the Thread class provides another version of the Abort() method. Its syntax is:

public void Abort(object stateInfo);

This version takes an argument that can hold and provide more details about the operation.

A Parameter for a Thread

Introduction

Your application may have to deal with many and different types of values, objects, or lists, etc. As mentioned already, one of the problems that may occur is that while the application is busy with one of those values/objects, the system might be iddle, or at least some operations will have to wait. As we have seen in previous sections, a solution is to create one or more threads and let the (or each) thread take care of one or more specific operations. In this case, you can ask a thread to deal with a specific value, to perform a certain operation, to carry a heavy object, or to process a long list of values/objects. In other words, you can pass a value, an object, or a list to a thread.

To give you the a bility to pass a value to a thread, the .NET Framework provides a delegate named ParameterizedThreadStart:

public delegate void ParameterizedThreadStart(object obj);

Passing a Value to a Thread

The method of this delegate expects a value or object as argument. Pass this argument as object.

In the method, the value can be used as a primitive type (integer, floating-point number, Boolean value, character, or string). One way you can use the argument is to display its value to the user. Here is an example:

using System.Web.Mvc;

namespace Exercise1.Controllers
{
    public class HomeController : Controller
    {
        private void Message(object value)
        {
            Response.Write(value);
        }
    }
}

You can declare a variable of type ParameterizedThreadStart and initialize it with its constructor that takes one argument. Pass the name of your method as argument. After that, declare a Thread variable initialize it with its constructor that takes one argument. Pass the ParameterizedThreadStart variable as argument. As mentioned previously, to execute the thread, call the Thread.Start() method. Here is an example:

using System.Web.Mvc;
using System.Threading;

namespace Exercise1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Title = "Home Page";

            return View();
        }

        private void Message(object value)
        {
            Response.Write(value);
        }

        public ActionResult Valuable()
        {
            ParameterizedThreadStart pts = new ParameterizedThreadStart(Message);

            Thread thMessage = new Thread(pts);

            thMessage.Start("Success is no accident. It is hard work, perseverance, learning, studying, sacrifice and most of all, love of what you are doing or learning to do. - Pele -");

            return View();
        }
    }
}

Passing an Object to a Thread

The ParameterizedThreadStart delegate accepts an object of any type as argument. To make this happen, you can create a class that represents the object, then define the object. You will pass that object as argument when calling the Thread.Start(). In the method that will be associated with the delegate, you should first cast the argument to your class, and then use your object as you see fit. Here is an example of passing an object to a thread:

using System;
using System.Web.Mvc;
using System.Threading;

namespace Exercise1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Title = "Home Page";

            return View();
        }

        private void ValidatePassword(object value)
        {
            int digits = 0;
            int symbols = 0;
            int lowercase = 0;
            int uppercase = 0;
            string conclusion = string.Empty;

            Employee staff = (Employee)value;
            string password = staff.Password;

            for (int i = 0; i < password.Length; i++)
            {
                if (Char.IsDigit(password[i]))
                {
                    digits++;
                }

                if (Char.IsSymbol(password[i]) || Char.IsPunctuation(password[i]))
                {
                    symbols++;
                }

                if (Char.IsLower(password[i]))
                {
                    lowercase++;
                }

                if (Char.IsUpper(password[i]))
                {
                    uppercase++;
                }
            }

            if (digits < 2)
                conclusion = "Your password must include at least 2 digits." + Environment.NewLine;
            if (symbols < 2)
                conclusion += "Your password must include at least 2 special characters." + Environment.NewLine;
            if(lowercase < 2)
                conclusion += "Your password must include at least 2 lowercase characters." + Environment.NewLine;
            if (uppercase < 2)
                conclusion += "Your password must include at least 2 uppercase characters.";

            ViewBag.Conclusion = conclusion;
        }

        public ActionResult AccountValidation()
        {
            Employee empl = new Employee()
            {
                EmployeeNumber = 938405,
                FirstName = "James",
                LastName = "Eubanks",
                Password = "P@sswrd#1"
            };

            ParameterizedThreadStart pts = new ParameterizedThreadStart(ValidatePassword);

            Thread thMessage = new Thread(pts);

            thMessage.Start(empl);

            return View();
        }
    }

    public class Employee
    {
        public long   EmployeeNumber { get; set; }
        public string FirstName      { get; set; }
        public string LastName       { get; set; }
        public string Username       { get; set; }
        public string Password       { get; set; }
    }
}
--------------------------------------------------------------
@{
    ViewBag.Title = "Account Validation";
}

<h2>Employee Account Valication</h2>

<p>@ViewBag.Conclusion</p>

Passing a Collection to a Thread

If some parts of your application are dealing with one or more large lists or a heavy database, you can create threads that each deal with a specific aspect. You can pass a list to the necessary threads. The list can be an array of numbers, a group of employees, a collection of cars, etc.

To prepare a list of values to be dealt with by a thread, create a void method that takes an object as argument. In the section where you want to use the thread, create a ParameterizedThreadStart object. Pass your method as argument. Create a Thread variable and pass your ParameterizedThreadStart variable to it. When calling the Thread.Start() method, pass your list to it. Here is an example:

using System;
using System.Web.Mvc;
using System.Threading;
using System.Collections.Generic;

namespace Exercise1.Controllers
{
    public class RestaurantController : Controller
    {
        // GET: Restaurant
        public ActionResult Index()
        {
            return View();
        }

        private void Showing(object values)
        {
            List<FoodItem> foods = (List<FoodItem>)values;

            string strResult = string.Empty;

            Response.Write("<h2>Display Restaurant Menu</h2>");

            Response.Write("<table class='table table-hover'>");
            Response.Write("<tr><td>Item Code</td><td>Food Item Name</td><td>Spicy?</td><td>Unit Price</td></tr>");

            foreach(FoodItem fi in foods)
            {
                Response.Write("<tr><td>" + fi.ItemCode + "</td><td>" + fi.FoodName + "</td><td>" + fi.Spicy + "</td><td>" + fi.UnitPrice + "</td></tr>");
            }

            Response.Write("</table>");
        }

        // GET: Restaurant/DisplayMenu
        public ActionResult DisplayMenu()
        {
            List<FoodItem> inventory = new List<FoodItem>()
            {
                new FoodItem() { ItemCode = "LS01", FoodName = "Sweet and Sour Chicken", Spicy = false, UnitPrice =  5.95 },
                new FoodItem() { ItemCode = "SP01", FoodName = "Wonton Soup",            Spicy = false, UnitPrice =  1.75 },
                new FoodItem() { ItemCode = "AP01", FoodName = "Egg Roll",               Spicy = false, UnitPrice =  1.75 },
                new FoodItem() { ItemCode = "LS02", FoodName = "Cashew Chicken",         Spicy = true,  UnitPrice =  6.95 },
                new FoodItem() { ItemCode = "CS01", FoodName = "Honey Walnut",           Spicy = false, UnitPrice = 10.95 },
                new FoodItem() { ItemCode = "SP02", FoodName = "Egg Drop Soup",          Spicy = false, UnitPrice =  1.95 },
                new FoodItem() { ItemCode = "LS03", FoodName = "Kung Pao Chicken",       Spicy = false, UnitPrice =  5.95 },
                new FoodItem() { ItemCode = "LS04", FoodName = "Szechuan Chicken",       Spicy = true,  UnitPrice =  6.25 }
            };

            ParameterizedThreadStart pts = new ParameterizedThreadStart(Showing);

            Thread thMessage = new Thread(pts);

            thMessage.Start(inventory);

            return View();
        }
    }

    public class FoodItem
    {
        public string ItemCode  { get; set; }
        public string FoodName  { get; set; }
        public bool   Spicy     { get; set; }
        public double UnitPrice { get; set; }
    }
}
--------------------------------------------------------------------------
@{
    ViewBag.Title = "Restaurant Menu";
}

Using a Parameterized Thread

Instead of first declaring a ParameterizedThreadStart variable and passing the name of a method to it, you can pass that method directly to a Thread variable. Here is an example:

using System.Web.Mvc;
using System.Threading;
using System.Collections.Generic;

namespace Exercise1.Controllers
{
    public class RestaurantController : Controller
    {
        // GET: Restaurant
        public ActionResult Index()
        {
            return View();
        }

        private void Showing(object values)
        {
            LinkedList<FoodItem> foods = (LinkedList<FoodItem>)values;

            string strResult = string.Empty;

            Response.Write("<h2>Display Restaurant Menu</h2>");

            Response.Write("<table class='table table-hover'>");
            Response.Write("<tr><td>Item Code</td><td>Food Item Name</td><td>Spicy?</td><td>Unit Price</td></tr>");

            foreach(FoodItem fi in foods)
            {
                Response.Write("<tr><td>" + fi.ItemCode + "</td><td>" + fi.FoodName + "</td><td>" + fi.Spicy + "</td><td>" + fi.UnitPrice + "</td></tr>");
            }

            Response.Write("</table>");
        }

        // GET: Restaurant/DisplayMenu
        public ActionResult DisplayMenu()
        {
            LinkedList<FoodItem> inventory = new LinkedList<FoodItem>();

            inventory.AddLast(new FoodItem() { ItemCode = "LS01", FoodName = "Sweet and Sour Chicken", Spicy = false, UnitPrice =  5.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "SP01", FoodName = "Wonton Soup", Spicy = false, UnitPrice = 1.75 });
            inventory.AddLast(new FoodItem() { ItemCode = "AP01", FoodName = "Egg Roll", Spicy = false, UnitPrice = 1.75 });
            inventory.AddLast(new FoodItem() { ItemCode = "LS02", FoodName = "Cashew Chicken", Spicy = true, UnitPrice = 6.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "CS01", FoodName = "Honey Walnut", Spicy = false, UnitPrice = 10.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "SP02", FoodName = "Egg Drop Soup", Spicy = false, UnitPrice = 1.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "LS03", FoodName = "Kung Pao Chicken", Spicy = false, UnitPrice = 5.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "AP02", FoodName = "Vegetable Spring Roll", Spicy = false, UnitPrice = 2.05 });
            inventory.AddLast(new FoodItem() { ItemCode = "LS04", FoodName = "Szechuan Chicken",       Spicy = true,  UnitPrice =  6.25 });
            inventory.AddLast(new FoodItem() { ItemCode = "CS02", FoodName = "Sesame Chicken", Spicy = true, UnitPrice = 9.75 });

            // ParameterizedThreadStart pts = new ParameterizedThreadStart(Showing);

            Thread thMessage = new Thread(Showing);

            thMessage.Start(inventory);

            return View();
        }
    }

    public class FoodItem
    {
        public string ItemCode  { get; set; }
        public string FoodName  { get; set; }
        public bool   Spicy     { get; set; }
        public double UnitPrice { get; set; }
    }
}

As is done with delegates and lambda expressions, instead of first creating a method, you can include it directly in the constructor of the ParameterizedThreadStart variable. Here is an example:

using System.Web.Mvc;
using System.Threading;
using System.Collections.Generic;

namespace Exercise1.Controllers
{
    public class RestaurantController : Controller
    {
        // GET: Restaurant
        public ActionResult Index()
        {
            return View();
        }

        // GET: Restaurant/DisplayMenu
        public ActionResult DisplayMenu()
        {
            LinkedList<FoodItem> inventory = new LinkedList<FoodItem>();

            inventory.AddLast(new FoodItem() { ItemCode = "LS01", FoodName = "Sweet and Sour Chicken", Spicy = false, UnitPrice =  5.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "SP01", FoodName = "Wonton Soup", Spicy = false, UnitPrice = 1.75 });
            inventory.AddLast(new FoodItem() { ItemCode = "AP01", FoodName = "Egg Roll", Spicy = false, UnitPrice = 1.75 });
            inventory.AddLast(new FoodItem() { ItemCode = "LS02", FoodName = "Cashew Chicken", Spicy = true, UnitPrice = 6.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "CS01", FoodName = "Honey Walnut", Spicy = false, UnitPrice = 10.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "SP02", FoodName = "Egg Drop Soup", Spicy = false, UnitPrice = 1.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "LS03", FoodName = "Kung Pao Chicken", Spicy = false, UnitPrice = 5.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "AP02", FoodName = "Vegetable Spring Roll", Spicy = false, UnitPrice = 2.05 });
            inventory.AddLast(new FoodItem() { ItemCode = "LS04", FoodName = "Szechuan Chicken",       Spicy = true,  UnitPrice =  6.25 });
            inventory.AddLast(new FoodItem() { ItemCode = "CS02", FoodName = "Sesame Chicken", Spicy = true, UnitPrice = 9.75 });

            ParameterizedThreadStart pts = new ParameterizedThreadStart((object values) =>
            {
                LinkedList<FoodItem> foods = (LinkedList<FoodItem>)values;

                string strResult = string.Empty;

                Response.Write("<h2>Display Restaurant Menu</h2>");

                Response.Write("<table class='table table-hover'>");
                Response.Write("<tr><td>Item Code</td><td>Food Item Name</td><td>Spicy?</td><td>Unit Price</td></tr>");

                foreach (FoodItem fi in foods)
                {
                    Response.Write("<tr><td>" + fi.ItemCode + "</td><td>" + fi.FoodName + "</td><td>" + fi.Spicy + "</td><td>" + fi.UnitPrice + "</td></tr>");
                }

                Response.Write("</table>");
            });

            Thread thMessage = new Thread(pts);

            thMessage.Start(inventory);

            return View();
        }
    }

    public class FoodItem
    {
        public string ItemCode  { get; set; }
        public string FoodName  { get; set; }
        public bool   Spicy     { get; set; }
        public double UnitPrice { get; set; }
    }
}

You can also create the whole ParameterizedThreadStart object in the constructor of the Thread variable. Here is an example:

using System.Web.Mvc;
using System.Threading;
using System.Collections.Generic;

namespace Exercise1.Controllers
{
    public class RestaurantController : Controller
    {
        // GET: Restaurant
        public ActionResult Index()
        {
            return View();
        }

        // GET: Restaurant/DisplayMenu
        public ActionResult DisplayMenu()
        {
            LinkedList<FoodItem> inventory = new LinkedList<FoodItem>();

            inventory.AddLast(new FoodItem() { ItemCode = "LS01", FoodName = "Sweet and Sour Chicken", Spicy = false, UnitPrice =  5.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "SP01", FoodName = "Wonton Soup", Spicy = false, UnitPrice = 1.75 });
            inventory.AddLast(new FoodItem() { ItemCode = "AP01", FoodName = "Egg Roll", Spicy = false, UnitPrice = 1.75 });
            inventory.AddLast(new FoodItem() { ItemCode = "LS02", FoodName = "Cashew Chicken", Spicy = true, UnitPrice = 6.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "CS01", FoodName = "Honey Walnut", Spicy = false, UnitPrice = 10.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "SP02", FoodName = "Egg Drop Soup", Spicy = false, UnitPrice = 1.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "LS03", FoodName = "Kung Pao Chicken", Spicy = false, UnitPrice = 5.95 });
            inventory.AddLast(new FoodItem() { ItemCode = "AP02", FoodName = "Vegetable Spring Roll", Spicy = false, UnitPrice = 2.05 });
            inventory.AddLast(new FoodItem() { ItemCode = "LS04", FoodName = "Szechuan Chicken",       Spicy = true,  UnitPrice =  6.25 });
            inventory.AddLast(new FoodItem() { ItemCode = "CS02", FoodName = "Sesame Chicken", Spicy = true, UnitPrice = 9.75 });
            
            Thread thMessage = new Thread(new ParameterizedThreadStart((object values) =>
            {
                LinkedList<FoodItem> foods = (LinkedList<FoodItem>)values;

                string strResult = string.Empty;

                Response.Write("<h2>Display Restaurant Menu</h2>");

                Response.Write("<table class='table table-hover'>");
                Response.Write("<tr><td>Item Code</td><td>Food Item Name</td><td>Spicy?</td><td>Unit Price</td></tr>");

                foreach (FoodItem fi in foods)
                {
                    Response.Write("<tr><td>" + fi.ItemCode + "</td><td>" + fi.FoodName + "</td><td>" + fi.Spicy + "</td><td>" + fi.UnitPrice + "</td></tr>");
                }

                Response.Write("</table>");
            }));

            thMessage.Start(inventory);

            return View();
        }
    }

    public class FoodItem
    {
        public string ItemCode  { get; set; }
        public string FoodName  { get; set; }
        public bool   Spicy     { get; set; }
        public double UnitPrice { get; set; }
    }
}

Previous Copyright © 2016-2019, FunctionX Next