Background threads in ASP.net applications (Part 1 – the concept application)
Published Aug 28 2019 07:50 AM 5,497 Views
Microsoft

When debugging memory dumps from customers, I have come to see, quite often, a pattern that you should not use in your application if you don’t want to run into trouble. This pattern can be resumed in a simple rule: thou shall not create thy own threads in thy application! To show you what I mean, through a series of three blog articles I will create a simple application and show you what happens when we transgress this rule.

Let’s build a simple ISS (International Space Station) tracker application. I am basing my sample on a sample You can see a complete website that plots the position of the ISS on the map using the same API on this website: https://wheretheiss.at/. The sample code for the application we are about to write, can be downloaded from OneDrive here:


Background Threading Sample Applications

When you start the solution, you should see a page that allows to get telemetry data from the space station (which is catalogued as satellite 25544). Here is the page loaded in Internet Explorer and showing the same telemetry data:

clipboard_image_0.png

So how does this work? The page has a button called btnGetSatelliteInfoNow which, when clicked, will submit the page and run some code behind to fetch certain telemetry data for satellite 25544, which happens to be the International Space Station. The code behind has a click event associated to the button, which looks like this:

    protected void btnGetSatelliteInfoNow_Click(object sender, EventArgs e)
    {
        var satellite = FetchSatelliteInfo();
        lblTime.Text = UnixTimeStampToDateTime(satellite.timestamp).ToString();
        lblAltitude.Text = satellite.altitude.ToString();
        lblLatitude.Text = satellite.latitude.ToString();
        lblLongtitude.Text = satellite.longitude.ToString();
        lblVelocity.Text = satellite.velocity.ToString();
        pnlResults.Visible = true;
    } 

The code will call a method called FetchSatelliteInfo(), here is the code for this second method:

    private Satellite FetchSatelliteInfo()
    {
        string url = "https://api.wheretheiss.at/v1/satellites/25544";
        var request = (HttpWebRequest)WebRequest.Create(url);
        var reader = new StreamReader(request.GetResponse().GetResponseStream());
        string content = reader.ReadToEnd();
        reader.Close();
        var satellite = JsonConvert.DeserializeObject(content);
        return satellite;
    } 

The method will compose a request to the ISS API url – (notice the url contains the NORAD catalog id for the ISS, which is 25544), then sends the request to retrieve telemetry information as a JSON object. The JSON response containing the satellite information from ISS API should look something like this:

{
   "name":"iss",
   "id":25544,
   "latitude":42.2825687563,
   "longitude":18.004509864333,
   "altitude":424.54036183209,
   "velocity":27573.416280786,
   "visibility":"daylight",
   "footprint":4530.4910258379,
   "timestamp":1566134760,
   "daynum":2458714.0597222,
   "solar_lat":13.073971650332,
   "solar_lon":339.48156331088,
   "units":"kilometers"
} 

Then the code will attempt to read the response stream using System.IO.StreamReader reader, so it can be deserialized to a POCO ((short for Plain Old CRL Object) class that will represent the ISS’s telemetry at a given time.

Here is the code listing:

[JsonObject(MemberSerialization.OptIn)]
public class Satellite
{
    [JsonProperty]
    public string name { get; set; }

    [JsonProperty]
    public int id { get; set; }

    [JsonProperty]
    public double Latitude { get; set; }

    [JsonProperty]
    public double Longitude { get; set; }

    [JsonProperty]
    public double Altitude { get; set; }

    [JsonProperty]
    public double Velocity { get; set; }

    [JsonProperty]
    public string Visibility { get; set; }

    [JsonProperty]
    public double Footprint { get; set; }

    [JsonProperty ]
    public double Timestamp { get; set; }

    [JsonProperty]
    public double Daynum { get; set; }

    [JsonProperty]
    public double Solarlat { get; set; }

    [JsonProperty]
    public double Solarlon { get; set; }

    [JsonProperty]
    public string Units { get; set; }

}

This class only contains a couple of properties that expose both get and set so that we can store the data related to a satellite (in this case the ISS) inside an instance of the class and modify it later if we need to. This will represent our Business Layer (Model).


Following the execution of FetchSatelliteInfo, the rest of the event handler method for the button click will just display the values of the Satellite object instance which we have retrieve on the page.

To continue the development of the application, we will look at how to ‘optimize’ the code that I have proposed so far in this artilce. The objective would be to have the application load the data in the background via a thread that would update the telemetry data for the space station every X seconds.

To do this, what I have seen many of my customers do, is to spawn a new .Net thread in the background and have this thread run an infinite loop with the following pseudo-logic:

  1. When the application loads up, start the background thread and have it execute the infinite loop
  2. Once in the infinite loop, the thread will the telemetry data by making API calls
  3. When it has retrieved the information for the telemetry data, it will go to sleep for a few seconds.
  4. It will then resume the loop from the beginning and attempt to refresh the coordinates

So let’s look how this pseudo logic can be transformed into code. Keep in mind that I am doing the bare minimum to keep the application as simple as possible and focus on the essentials.

When the application loads into the process memory (into the w3wp.exe worker process), the Application_Start event is fired. This seems like a good place to add the code to create a background thread.


The first method I will implement is located in the Global.asax file (but I could have implemented this inside another class if I had wanted to as well). This method is called UpdateSatelliteInfo. Here is the code listing:

    private void UpdateSatelliteInfo()
    {
        string url = "https://api.wheretheiss.at/v1/satellites/25544";
        Satellite satellite; string content; HttpWebRequest request; StreamReader reader;
        do
        {

            request = (HttpWebRequest)WebRequest.Create(url);
            reader = new StreamReader(request.GetResponse().GetResponseStream());
            content = reader.ReadToEnd();
            reader.Close();
            satellite = JsonConvert.DeserializeObject(content);

            satellite.Time = UnixTimeStampToDateTime(satellite.Timestamp);
            Application["satellite"] = satellite;

            //sleep for 5 Seconds
            System.Threading.Thread.Sleep(5000);
        } while (true);
    }

What the method does is that it will update the Satellite Info (just to keep it simple – we could use something more dynamic afterwards) that will be refreshed every X seconds. Following this declaration, it will go into an infinite loop, represented by the do{} while(true) statement.


The Satellite telemetry data will be inspected, and we will try to obtain the current position for the ISS, by making a call to the ISS API using the StreamReader object. With every iteration of the while loop we compose the correct URL and then we ask the StreamReader object to retrieve the JSON datagram corresponding to the current state of the Satellite.


Once we have loaded the JSON content from the StreamReader inside the string object, the method will create a new instance of the Satellite class and populate its properties with data extracted from the JSON – via the JsonConvert.DeserializeObject<Satellite>(content) method, just like in the previous sample.


Note that we have to convert date time from the loaded JSON object, which is in unix format and which will be mapped by the JSON deserialization into a property of type Double, to an instance of type DateTime. To do so, I have created a method named UnixTimeStampToDateTime():

public static DateTime UnixTimeStampToDateTime(double unixTimeStamp)
    {
        // Unix timestamp is seconds past epoch
        System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0);
        dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime();
        return dtDateTime;
    }

The newly created instance of the Satellite object will be added to the HttpApplication object, an object that is available in all parts of the application and that can be thought of as a global dictionary of variables. (Some of you might remark that I could have checked if I already had an entry for the Satellite inside the Application object and if so, I could just have performed an update on the object instead of creating a new instance every time, and you would be right to do so. However, in essence I am trying to keep the app basic, and not necessarily optimized).


Following the loading of the Satellite data, the loop will put the thread to sleep for 5 seconds. After this, the loop will iterate again and again, until the end of time, or the end of the process, whichever comes first.


Now for the interface to display Satellite data in a web-page. To do this, I have created a second page for the application, called AutoSatellite.aspx. This page contains a repeater control that is strongly–typed data-bound to an object of type Satellite, via the ItemType property. If you are new to strongly typed data-binding, I would suggest you have a look at my ASP.net 4.5 Webforms video tutorial:

http://linqto.me/n/ASPNet45


Here are the tags for the repeater control – note the ItemType tag that indicates the type of object that will be displayed by the control and the SelectMethod tag will indicate which method in the code behind is to be called to load the elements to display when the control is rendered:

clipboard_image_0.png

 

Inside the repeater control, we just display a fieldset tag for the Satellite Info, and inside this we display, the Time, the Alttitude and other information via strongly typed data-binding.


Inside the repeater control, we just display a fieldset tag for the Satellite Info, and inside this we display, the Time, the Alttitude and other information via strongly typed data-binding.

In the code behind, the GetSatellite() method does the loading of the satellite data from the Application object – here is the code:

     
 public Satellite GetSatellite()
    {
        //get the Satellite from the application object
        Satellite satellite = new Satellite();
        if (Application["satellite"] != null)
        {
            satellite = (Satellite)Application["satellite"];
        }
        return satellite;

    } 


The method declares a variable of type Satellite and attempts to assign the satellite info from the Application object to the newly created object. We check if satellite info in the Application object is not null. Should the background thread not have finished loading the satellite info for the first time before we show the page, we don’t want to show null data and crash the page, hence the reason for the test. It ensures that the data was loaded by the background thread at least once.

 

There is also a refresh button on the page, with a Click event handler in the code behind:

    protected void cmdRefresh_Click(object sender, EventArgs e)
    {
        //do nothing
        rptSatellite.DataBind();
    }

All this does is that it forces the repeater control to data-bind once again – and hence to call the GetSatellite() method and reload the data from the Application object. Note that the data will not be reloaded by the background thread when this new call to GetSatellite() comes in. It will be refreshed only when the background thread wakes up the next time around and loads new data from ISS.


You can find the sample completed with this new code in my OneDrive as well, by following this link:

https://onedrive.live.com/?id=9A83CB6EACC8118C%215695&cid=9A83CB6EACC8118C

In the next installment, I will discuss the problems with this kind of pattern and why it is not recommended you make use of it in your ASP.net applications.

 

written by: Paul Cociuba
reviewed by: Muna AlHasan

 

 

 

 

 

Version history
Last update:
‎Aug 28 2019 07:59 AM
Updated by: