0

After an IISRESET on my public facing webserver, the initialization of my app seems correct. That is, in the Application_Start event I launch a new "email" thread whose purpose is to sleep until the configured time, upon "waking" produce a report and email it to my administrative user(s) and go back to sleep until the configured duration elapses at which time, the report is created again and emailed out. I am currently configured to start at 1900 and produce a new report every 12 hours.

As time marches on with this production site, however, something is causing an "extra" thread to be created. This, in turn, results in a duplicate report being emailed. While the problem is benign enough, I'd like to clean this up if possible. Here's a snippet:

Public Class [Global]
Inherits System.Web.HttpApplication
Public emth As New EmailThread
Private vcomputer As String
Private eventsource As String
Private message1 As String
Public MyInstanceStart As New ThreadStart(AddressOf emth.workerThread)
Public InstanceCaller As New Thread(MyInstanceStart)

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
    vcomputer = System.Environment.MachineName  
    InstanceCaller.Name = "EMAILBOT"
    InstanceCaller.Start()
    UIF.WriteToLog("Application_Start: EMAILBOT instance Started")
End Sub

Examining the Application Event Log on the webserver, I can see the above message properly upon an IISRESET. This is followed by another "proper" logged event in the workerThread which tells us how long the code will sleep before the next report time. Here is a snippet from the workerThread:

Public Class EmailThread

Public Sub workerThread()
    Dim TimeToStart As Date
    Dim TimeToStart_Next As Date
    Try
        Dim Start As Integer = CInt(ConfigurationManager.AppSettings("EmailStartTime"))
        Dim iSleep As Integer = CInt(ConfigurationManager.AppSettings("RobotIntervalHours"))
        If Start < 1 Or Start > 23 Then Start = 12 
        If iSleep < 1 Or iSleep > 24 Then iSleep = 12

        TimeToStart = New Date(Now.Year, Now.Month, Now.Day, Start, 0, 0)
        Do Until TimeToStart > Now
            'We missed the start time by some amount...
            ' compute new time to start by adding RobotIntervalHours
            TimeToStart = DateAdd(DateInterval.Hour, iSleep, TimeToStart)
        Loop
        TimeToStart_Next = DateAdd(DateInterval.Hour, iSleep, TimeToStart)
        '    'Set NEXT TimeToStart for reporting
        '   Compute how long to wait now
        Dim SleepMinutes As Long
        While 1 = 1
            SleepMinutes = System.Math.Abs(DateDiff(DateInterval.Minute, Now, TimeToStart))
            strScheduledStart = FormatDateTime(TimeToStart, DateFormat.GeneralDate)
            UIF.WriteToLog("EmailThread will sleep for " & CStr(SleepMinutes) & " minutes; " & _
                           "waking at next starttime: " & strScheduledStart)
            Thread.CurrentThread.Sleep(TimeSpan.FromMinutes(SleepMinutes))
            '---------------------------------------------------------------------------------------
            '   Upon waking, resume here:
            '---------------------------------------------------------------------------------------
            TimeToStart = TimeToStart_Next
            TimeToStart_Next = DateAdd(DateInterval.Hour, iSleep, TimeToStart)
            BC.NextRobotStartTime = FormatDateTime(TimeToStart, DateFormat.GeneralDate)
            StartRobotProcess(strScheduledStart)                    'Robot reports generated
            SleepMinutes = 0           
        End While
    Catch ex As Exception
        UIF.LogException(ex, "CRITICAL: Exception in 'EmailThread.workerThread' has been logged:", 100)
    End Try
End Sub

The above code seems to work fine (like I stated, after an IISRESET, I see the one logged event from the Application_Start event followed by the log entry from my "email" thread: "EmailThread will sleep for nnn minutes...etc.". But somehow (over time) I am getting another instance of the EmailThread thus producing 2 reports instead of just one at the scheduled "wake time".

In the IIS application pool assigned to this website, I have the following settings:

- Recycle worker processes (in minutes) is UNCHECKED
- Recycle worker process (number of requests) is UNCHECKED
- Recycle worker process (at various times) is UNCHECKED / nothing specified
- [Idle timeout] Shutdown worker process after being idle for (time in minutes) is UNCHECKED

I have noticed that the Application_Start event can be entered again (in one case roughly 38 minutes after the first time) which causes my code to run again and create another [and unwanted] thread.

I would appreciate suggestions on how to tighten this up to make this symptom go away.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
John Adams
  • 4,773
  • 25
  • 91
  • 131
  • Is the use of a thread that spends 99% of its life asleep really the best way? Can you not use a timer and do your work in the elapsed event? At any rate, you could always use a static property that you set when you start your thread and clear when you kill it when your application ends. Just don't start more threads if that property is set (for instance a boolean.) – Erik Noren Dec 07 '10 at 22:57
  • Also, I assume you're using only 1 server (with no plans to add additional servers) to host your application. Each one would start their own thread and try to send the email. – Erik Noren Dec 07 '10 at 22:58

2 Answers2

1

What I can think of is that in your application in IIS, you have an extra application (converting virtual folder to Application) in one of the subfolders of your website.

ASP.NET performance counters can also help you with the number of applications and threads created. Use perfmon to monitor them.

Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • I believe you may be correct about the actual "cause" of this situation (I think I've "solved" the immediate problem by adding a SHARED (i.e. static) boolean set upon creating the thread and then checked to prevent a 2nd start of the thread. I do, in fact, have something like you say. My default web site, Home Directory tab, points the local path to: c:\zVersion7\Site. I have a subordinate Virtual Directory called Zipeee pointing to same local path. The "reason" for this is I want www.zipeee.com and www.zipeee.com/zipeee to point to same code; has something to do with host headers on this srvr – John Adams Dec 09 '10 at 21:25
  • I suspect something in this setup is the cause; I will be exploring performance counters but perhaps you can suggest how to get rid of the need for the extra virtual directory? – John Adams Dec 09 '10 at 21:27
  • I think both your applications run in the same application pool. Create separate application pools for them and your problem will be sorted. – Aliostad Dec 09 '10 at 23:28
  • I rewrote the code with ideas from the 1st answer and the problem remained. I tried changing the application pools assigned and still had problem. Then I re-read Aliostad's initial reply on Dec 7 more closely and examined this website. Sure enough, there were two virtual directories within this default website that were marked as "Applications". One was for an Upload directory and I think unrelated to this issue but the other was to the same physical directory as the application home directory. REMOVEing the "application name" seems to have fixed it. I am happy but wish I understood better WHY – John Adams Jan 14 '11 at 19:33
  • @John pleased to hear you got the result. Well, understanding it why is not difficult - and that is how I got the hunch about the problem. When you convert a virtual folder to application, this will create a separate application running inside its own AppDomain. That is why you would see parallel events each in its own AppDomain. – Aliostad Jan 15 '11 at 10:21
0

Doesn't figure out why, but it would be easy to check a flag in the application object, check it during startup and, if set, return.

verisimilidude
  • 738
  • 3
  • 8