Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 6873151
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 27, 20262026-05-27T04:01:27+00:00 2026-05-27T04:01:27+00:00

Description: On a C# ASP.Net web application, we have implemented some timers to periodically

  • 0

Description:

On a C# ASP.Net web application, we have implemented some timers to periodically run background tasks. One of the timers occasionally seems to get “doubled” or more rarely “tripled”.

The timer is set to run once every minute and seems to run properly for a while. Eventually, however, it seems like a second timer gets started and calls the timed process a second time within the same time interval. I’ve even seen a case where we had three processes running.

Since this process locks some database records and having a second (or third) process doing the same thing will cause a deadlock or timeout error on the database connection, we’ve implemented a mechanism to only allow one thread at a time to execute the database critical portion of the process code. When the process takes longer than a minute to run, this mechanism successfully blocks the next run triggered by its own timer. But the thread locking fails if the process is triggered by the second (or third) timer.

In our logs, I output both the Process ID and the Managed Thread ID, which lets me see which thread is starting, finishing, or erring out. The strange thing, is that regardless of which timer instance kicked off the process, the Process ID is the same.

var processID = System.Diagnostics.Process.GetCurrentProcess().Id;
var thread = System.Threading.Thread.CurrentThread.ManagedThreadId;

How do I prevent multiple instances of the timer?

We have a web-farm with 2 servers behind a load balancer. I’ve been assurred that the web-garden is set to only allow one instance of the app-pool on each server. A web.config setting specifies which server will run the timed process. The other server will not load the timer.

Relevant Code:

On the Global.asax.cs

protected static WebTaskScheduler PersonGroupUpdateScheduler
{
    get;
    private set;
}

protected void StartSchedulers()
{
    using (var logger = new LogManager())
    {
        // ... other timers configured in similar fashion ...

        if (AppSetting.ContinuousPersonGroupUpdates)
        {
            // clear out-of-date person-group-updater lock
            logger.AppData.Remove("PersonGroupUpdater"); // database record to prevent interference with another process outside the web application.

            var currentServer = System.Windows.Forms.SystemInformation.ComputerName;
            if (currentServer.EqualsIngoreCase(AppSetting.ContinuousPersonGroupUpdateServer))
            {
                PersonGroupUpdateScheduler = new WebTaskScheduler() {
                    AutoReset = true,
                    Enabled = true,
                    Interval = AppSetting.ContinuousPersonGroupUpdateInterval.TotalMilliseconds,
                    SynchronizingObject = null,
                };
                PersonGroupUpdateScheduler.Elapsed += new ElapsedEventHandler(DistributePersonGroupProcessing);
                PersonGroupUpdateScheduler.Start();
                HostingEnvironment.RegisterObject(PersonGroupUpdateScheduler);
                logger.Save(Log.Types.Info, "Starting Continuous Person-Group Updating Timer.", "Web");
            }
            else
            {
                logger.Save(Log.Types.Info, string.Format("Person-Group Updating set to run on server {0}.", AppSetting.ContinuousPersonGroupUpdateServer), "Web");
            }
        }
        else
        {
            logger.Save(Log.Types.Info, "Person-Group Updating is turned off.", "Web");
        }
    }
}

private void DistributePersonGroupProcessing(object state, ElapsedEventArgs eventArgs)
{
    // to start with a clean connection, create a new data context (part of default constructor)
    // with each call.
    using (var groupUpdater = new GroupManager())
    {
        groupUpdater.HttpContext = HttpContext.Current;
        groupUpdater.ContinuousGroupUpdate(state, eventArgs);
    }
}

On a separate file, we have the WebTaskScheduler class which just wraps System.Timers.Timer and implements the IRegisteredObject interface so that IIS will recognize the triggered process as something it needs to deal with when shutting down.

public class WebTaskScheduler : Timer, IRegisteredObject
{
    private Action _action = null;
    public Action Action
    {
        get
        {
            return _action;
        }
        set
        {
            _action = value;
        }
    }

    private readonly WebTaskHost _webTaskHost = new WebTaskHost();

    public WebTaskScheduler()
    {
    }

    public void Stop(bool immediate)
    {
        this.Stop();
        _action = null;
    }
}

Finally, the locking mechanism for the critical section of the code.

public void ContinuousGroupUpdate(object state, System.Timers.ElapsedEventArgs eventArgs)
{
    var pgUpdateLock = PersonGroupUpdaterLock.Instance;

    try
    {
        if (0 == Interlocked.Exchange(ref pgUpdateLock.LockCounter, 1))
        {
            if (LogManager.AppData["GroupImporter"] == "Running")
            {
                Interlocked.Exchange(ref pgUpdateLock.LockCounter, 0);
                LogManager.Save(Log.Types.Info, string.Format("Group Import is running, exiting Person-Group Updater.  Person-Group Update Signaled at {0:HH:mm:ss.fff}.", eventArgs.SignalTime), "Person-Group Updater");
                return;
            }

            try
            {
                LogManager.Save(Log.Types.Info, string.Format("Continuous Person-Group Update is Starting.  Person-Group Update Signaled at {0:HH:mm:ss.fff}.", eventArgs.SignalTime), "Person-Group Updater");
                LogManager.AppData["PersonGroupUpdater"] = "Running";

                // ... prep work is done here ...

                try
                {
                    // ... real work is done here ...

                    LogManager.Save(Log.Types.Info, "Continuous Person-Group Update is Complete", "Person-Group Updater");

                }
                catch (Exception ex)
                {
                    ex.Data["Continuous Person-Group Update Activity"] = "Processing Groups";
                    ex.Data["Current Record when failure occurred"] = currentGroup ?? string.Empty;
                    LogManager.Save(Log.Types.Error, ex, "Person-Group Updater");
                }

            }
            catch (Exception ex)
            {
                LogManager.Save(Log.Types.Error, ex, "Person-Group Updater");
            }
            finally
            {
                Interlocked.Exchange(ref pgUpdateLock.LockCounter, 0);
                LogManager.AppData.Remove("PersonGroupUpdater");
            }

        }
        else
        {
            // exit if another thread is already running this method
            LogManager.Save(Log.Types.Info, string.Format("Continuous Person-Group Update is already running, exiting Person-Group Updater.  Person-Group Update Signaled at {0:HH:mm:ss.fff}.", eventArgs.SignalTime), "Person-Group Updater");
        }
    }
    catch (Exception ex)
    {
        Interlocked.Exchange(ref pgUpdateLock.LockCounter, 0);
        LogManager.Save(Log.Types.Error, ex, "Person-Group Updater");
    }
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-27T04:01:28+00:00Added an answer on May 27, 2026 at 4:01 am

    IIS can/will host multiple AppDomains under a worker process (w3wp). These AppDomains can’t/don’t/shouldn’t really talk to each. It’s IIS’s responsibility to manage them.

    I suspect what’s happening is that you have multiple AppDomains loaded.

    That said…just to be 100% sure…the timer is being started under Application_Start in your global.asax, correct? This will get executed once per AppDomain (not per HttpApplication, as it’s name suggests).

    You can check how many app domains are running for your process by using the ApplicationManager's GetRunningApplications() and get GetAppDomain(string id) methods.

    In theory you could also do some inter-appdomain communication in there to make sure your process only starts once…but I’d strongly advise against it. In general, relying on scheduling from a web application is ill advised (because your code is meant to be ignorant of how IIS manages your application lifetime).

    The preferred/recommended approach for scheduling is via a Windows Service.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have a ASP.NET Web Forms application that internally makes many SOAP and REST
VS 2008 / ASP.Net I have a deployed a ASP.Net Web Application on Windows
I have an ASP.Net Framework 4 web application making an interop call to a
I have created a Web Application in asp.net 2.0. which is working fine on
I have written an ASP.NET web application (not site) in Delphi Prism. Everything works
I have an ASP.NET Web Application which has a reference to 'C:\references\Utils.Varia.dll'. There is
I have created a new MVC2 project using the ASP.NET MVC2 Web Application, which
I have an ASP.NET application all things are working fine but after some minutes
In a C# ASP .NET application I have a web service that receives a
I have created a web application in ASP.Net 4.0 framework and use LinqtoSQL for

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.