【问题标题】:How to best debug the creation of unwanted threads in ASP.NET web application如何最好地调试在 ASP.NET Web 应用程序中创建不需要的线程
【发布时间】:2010-12-07 22:34:33
【问题描述】:

在我面向公众的网络服务器上执行 IISRESET 后,我的应用程序的初始化似乎是正确的。也就是说,在 Application_Start 事件中,我启动了一个新的“电子邮件”线程,其目的是休眠到配置的时间,在“唤醒”时生成一个报告并将其通过电子邮件发送给我的管理用户并返回休眠状态直到配置持续时间过后,将再次创建报告并通过电子邮件发送出去。我目前配置为从 1900 开始,每 12 小时生成一份新报告。

然而,随着时间的推移,这个生产站点出现了一些问题,导致创建了一个“额外的”线程。反过来,这会导致通过电子邮件发送重复的报告。虽然问题足够温和,但如果可能的话,我想清理它。这是一个sn-p:

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

检查网络服务器上的应用程序事件日志,我可以在 IISRESET 上正确看到上述消息。接下来是 workerThread 中的另一个“正确”记录事件,它告诉我们代码在下一次报告时间之前将休眠多长时间。这是来自workerThread的sn-p:

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

上面的代码似乎工作正常(就像我说的那样,在 IISRESET 之后,我看到 Application_Start 事件中的一个记录事件,然后是我的“电子邮件”线程中的日志条目:“EmailThread 将休眠 nnn 分钟.. .etc.”。但不知何故(随着时间的推移),我得到了另一个 EmailThread 实例,因此在预定的“唤醒时间”生成了 2 个报告,而不仅仅是一个。

在分配给本网站的IIS应用程序池中,我有如下设置:

- 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

我注意到 Application_Start 事件可以再次输入(在第一次之后大约 38 分钟),这会导致我的代码再次运行并创建另一个 [和不需要的] 线程。

对于如何收紧以使这种症状消失的建议,我将不胜感激。

【问题讨论】:

  • 使用 99% 的生命都在休眠的线程真的是最好的方法吗?您可以不使用计时器并在经过的事件中完成您的工作吗?无论如何,您始终可以使用在启动线程时设置的静态属性,并在应用程序结束时将其杀死时清除。如果设置了该属性(例如布尔值),请不要启动更多线程。
  • 另外,我假设您只使用 1 台服务器(没有计划添加额外的服务器)来托管您的应用程序。每个人都会开始自己的线程并尝试发送电子邮件。

标签: asp.net multithreading event-log application-start address-operator


【解决方案1】:

我能想到的是,在您在 IIS 中的应用程序中,您网站的一个子文件夹中有一个额外的应用程序(将虚拟文件夹转换为应用程序)。

ASP.NET 性能计数器还可以帮助您计算创建的应用程序和线程的数量。使用 perfmon 监控它们。

【讨论】:

  • 我相信您对这种情况的实际“原因”可能是正确的(我认为我已经通过在创建线程时添加一个共享(即静态)布尔集来“解决”眼前的问题,并且然后检查以防止线程的第二次启动。事实上,我确实有像你说的那样。我的默认网站,主目录选项卡,将本地路径指向:c:\zVersion7\Site。我有一个从属的虚拟名为 Zipeee 的目录指向相同的本地路径。“原因”是我希望 www.zipeee.com 和 www.zipeee.com/zipeee 指向相同的代码;与此 srvr 上的主机标头有关
  • 我怀疑这个设置中的某些东西是原因;我将探索性能计数器,但也许您可以建议如何摆脱对额外虚拟目录的需求?
  • 我认为您的两个应用程序都在同一个应用程序池中运行。为它们创建单独的应用程序池,您的问题将得到排序。
  • 我用第一个答案的想法重写了代码,问题仍然存在。我尝试更改分配的应用程序池,但仍然有问题。然后我更仔细地重新阅读了 Aliostad 在 12 月 7 日的初步回复,并检查了这个网站。果然,这个默认网站中有两个虚拟目录被标记为“应用程序”。一个是上传目录,我认为与这个问题无关,但另一个是与应用程序主目录相同的物理目录。删除“应用程序名称”似乎已修复它。我很高兴,但希望我能更好地理解为什么
  • @John 很高兴听到你得到了结果。好吧,理解它为什么并不困难——这就是我对这个问题的预感。当您将虚拟文件夹转换为应用程序时,这将创建一个在其自己的 AppDomain 中运行的单独应用程序。这就是为什么您会在各自的 AppDomain 中看到并行事件。
【解决方案2】:

不知道为什么,但很容易检查应用程序对象中的标志,在启动期间检查它,如果设置,则返回。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-02
    相关资源
    最近更新 更多