ASP.net global.asax Timers randomly stop working - c#

Given the code:
protected void Application_Start(object sender, EventArgs e)
{
var testTimer = new Timer(
LogTimer,
null,
new TimeSpan(0, 0, 0, 0),
new TimeSpan(0, 0, 0, 1)
);
}
public static void LogTimer(object sender)
{
"Hello".Log();
}
At seemingly random occasions the timer stops firing, and wont start again unless I restart the website.
It doesn't throw any exceptions, but looking in the Windows error log there are some entries:
The Open Procedure for service "Lsa" in DLL "C:\Windows\System32\Secur32.dll" failed. Performance data for this service will not be available. The first four bytes (DWORD) of the Data section contains the error code.
Unable to open the Server service performance object. The first four bytes (DWORD) of the Data section contains the status code.
The site is active (the start mode of the app pool is AlwaysRunning.
I understand that using timers in this way is not a recommended approach for critical things for exactly this reason, but I am failing to come up with an explanation as to why it's silently and apparently randomly just giving up.

From your code, I expect the garbage collector to collect your timer since there is no handle for that. have you tried something like
static Timer testTimer ;
protected void Application_Start(object sender, EventArgs e)
{
testTimer = new Timer(...);
}

ASP.NET isn't suited to running timers due to the way AppDomains get unloaded, the threading model and many other factors.
I suggest you read this blog post from Scott Hanselman that discusses various ways to successfully run timer-based code in ASP.NET web applications.

Related

ASP.net Framework and blocked threads

Can anyone explain the following behaviour of an ASP.net webform on IIS. When I have a page and start it 4 times, the pages are served synchroniously. In other words if this page takes about 10 seconds to build, I see the first page appear at around 10sec, 2nd at 20sec, 3rd at 30sec, 4th at 40sec. When asking the threadnumber I usually see 2 threadnumbers, which makes me think IIS/ASP.net is only having two threads in IIS per application pool?
Small example:
protected void Page_Load(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(10000);
this.Label1.Text = System.Threading.Thread.CurrentThread.ManagedThreadId.ToString();
}
When I look for suggestions on the internet I find that I should program asynchroniously and for example use await Task.Delay to not block the thread, but I still have the same result when I use this Page_Load:
protected async void Page_Load(object sender, EventArgs e)
{
await System.Threading.Tasks.Task.Delay(10000);
this.Label1.Text = System.Threading.Thread.CurrentThread.ManagedThreadId.ToString();
}
Can anyone explain me this behaviour? First of all if there are two threads I would expect two pages being served at 10sec and the 3rd and 4th at 20sec. But even when I run the code with async it doesn't really make much/any difference. How can I circumvent this blocking of threads with an easy solution?
Best regards,
Rémy Samulski
EDIT: I was calling the website from the same browser and the same URL. This caused the issue. Thx to Richard for helping me figuring out my mistake.

Terminating Thread Running an Event

I wrote an API that automates a certain website. However, on the testing stage, I noticed that (not very sure), my thread is not being terminated correctly.
I am using the WebBrowser object to navigate inside a thread, so that it works synchronously with my program:
private void NavigateThroughTread(string url)
{
Console.WriteLine("Defining thread...");
var th = new Thread(() =>
{
_wb = new WebBrowser();
_wb.DocumentCompleted += PageLoaded;
_wb.Visible = true;
_wb.Navigate(url);
Console.WriteLine("Web browser navigated.");
Application.Run();
});
Console.WriteLine("Thread defined.");
th.SetApartmentState(ApartmentState.STA);
Console.WriteLine("Before thread start...");
th.Start();
Console.WriteLine("Thread started.");
while (th.IsAlive) { }
Console.WriteLine("Journey ends.");
}
private void PageLoaded(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Console.WriteLine("Pages loads...");
.
.
.
switch (_action)
{
.
.
.
case ENUM.FarmActions.Idle:
_wb.Navigate(new Uri("about:blank"));
_action = ENUM.FarmActions.Exit;
return;
case ENUM.FarmActions.Exit:
Console.WriteLine("Disposing wb...");
_wb.DocumentCompleted -= PageLoaded;
_wb.Dispose();
break;
}
Application.ExitThread(); // Stops the thread
}
Here is how I call this function:
public int Attack(int x, int y, ArmyBuilder army)
{
// instruct to attack the village
_action = ENUM.FarmActions.Attack;
//get the army and coordinates
_army = army;
_enemyCoordinates[X] = x;
_enemyCoordinates[Y] = y;
//Place the attack command
_errorFlag = true; // the action is not complated, the flag will set as false once action is complete
_attackFlag = false; // attack is not made yet
Console.WriteLine("Journey starts");
NavigateThroughTread(_url.GetUrl(ENUM.Screens.RallyPoint));
return _errorFlag ? -1 : CalculateDistance();
}
So the problem is, when I call the Attack function, couple times like this:
_command.Attack(509, 355, new ArmyBuilder(testArmy_lc));
_command.Attack(509, 354, new ArmyBuilder(testArmy_lc));
_command.Attack(505, 356, new ArmyBuilder(testArmy_lc));
_command.Attack(504, 356, new ArmyBuilder(testArmy_lc));
_command.Attack(504, 359, new ArmyBuilder(testArmy_lc));
_command.Attack(505, 356, new ArmyBuilder(testArmy_lc));
_command.Attack(504, 356, new ArmyBuilder(testArmy_lc));
_command.Attack(504, 359, new ArmyBuilder(testArmy_lc));
My application most of the times, gets stuck in one of these function (usually happens after the 4th or 5th). When it gets stuck the last log that I see is
Web browser navigated.
I assume it is something to do with termination of my thread. Can someone show me how I can run a thread which runs the DocumentCompleted event ?
I don't see any obvious reason for deadlock, nor did it reproduce at all when testing the code. There are a number of flaws in the code but nothing that yells "here!" loudly. I can only make recommendations:
Consider that you do not need a thread at all. The while (th.IsAlive) { } hot loop blocks your main thread while you wait for the browser code to finish the job. That is not a useful way to use a thread, you might as well use your main thread. This instantly eliminates a large number of potential hang causes.
The state logic in PageLoaded is risky. We cannot see all of it but one glaring issue is that you dispose the WebBrowser twice. If you have a case where you use return without a Navigate() call then you'll hang as described. No need to unsubscribe the event but same story, if you do unsubscribe but don't all Application.Exit() then you'll hang as described. State machines can be hard to debug, thorough logging is necessary. Minimize the risk by moving the Dispose() call and unsubscribing the event out of the logic, it doesn't belong there. And you need to test what happens when any Navigate() call ends up in failure, redirecting to a page you did not expect.
The _wb.Dispose() call is risky. Note that you destroy the WebBrowser while its DocumentCompleted event is in flight. Technically that can return code execution to code that is no longer alive or present. That can trip a race condition in the browser. As well as in the debugger, there is a dedicated MDA that checks for this problem. It is trivially avoided by moving the Dispose() call after the Application.Run() call where it belongs.
The while-loop burns 100% core, potentially starving the worker thread. Not a good enough reason to explain deadlock, but certainly unnecessary. Use Thread.Join() instead.
You create a lot of WebBrowser objects in this code. It is a very heavy object, as you can imagine, you need to keep an eye on memory usage in your program. Especially the unmanaged kind. If the browser leaks, like they so often do, you could technically create a scenario where the WB initializes okay but does not have enough memory left to load the page. Strongly favor using only one WB.
You need to consider that this might well be an environmental problem. On the top of that list is forever anti-malware and firewall, they always have a very good reason to treat a browser specially since that is the most common malware injection vector. You'll need to run your test with anti-malware and firewall disabled to ensure that it is not the cause of the hang.
Another environmental problem is one I noticed while testing this code, Google got sulky about me hitting it so often and started to throttle the requests, greatly slowing down the code. Talk to the web site owner and ask if he's got similar blocking or throttling counter-measures in place, most do. You need to test your state logic to verify that it still works properly when the browser redirects to an error page.
Yet another environmental issue is the WB will display a dialog itself in certain cases. This can deadlock in 3rd party code, very hard to diagnose. You should at least set the WebBrower.ScriptErrorsSuppressed to true but beware of Javascript code in the web page you load that itself creates new windows or displays alert dialogs. Using one WB is the workaround.
Keep in mind that your program can only be as reliable as your Internet connection and the web page server. That's not a terribly good place to be of course, both are quite out of your reach and you don't get nice exceptions to help you diagnose such a failure. And consider that you probably have not yet tested your program well enough yet to check if it can survive such a failure, it doesn't happen enough.
Quite a laundry list, focus first on eliminating the unnecessary thread and temporarily suppressing anti-malware. That's quick, focus next on using only one WebBrowser.
Hans thank you, I was able to fix this issue with one of your ideas. As you spent your time giving me a long answer, I wanted respond in same manner.
2 - I built the state machine structure carefully and with a lot logs (you can see it from my git account) also did a lot of debugs. I am sure that after I'm done navigating, I use Application.ExitThread() and wb.Dispose() only once.
3 - I tried placing the wb.Dispose() outside the event, however I couldn't find any other place where the Thread is still alive. If I try disposing WebBrowser outside the thread which is created inside the thread, the application gives me an error.
4 - I changed the code while (th.IsAlive) { } with th.Join(2000) this is absolutely a better idea but did not change anything. It optimized the code and as you mentioned, it prevented burning 100% core of my CPU.
5 - I tried using a single WebBrowser object which is instantiated in the constructor. However when I tried to navigate inside the thread, the application wouldnt even fire the events anymore. For some reason, I couldn't make it running whit a single WB object.
6,7 - I tested my application with different PC's and diffrent networks(with firewall and non-firewall protection). I changed windows firewall options as well but no travail. On my original code I do have _wb.ScriptErrorsSuppressed = true; so this shouldn't also be the issue.
8,9 - If these are the reasons, I can't do anything about it. But I doubt the real problem is caused because of them.
1 - This one was a good suggestion. I tried implementing my code without using a thread and it is now working fine. Here is how it looks like (still needs a lot optimization)
// Constructer
public FarmActions(string token)
{
// set the urls using the token
_url = new URL(token);
// define web browser properties
_wb = new WebBrowser();
_wb.DocumentCompleted += PageLoaded;
_wb.Visible = true;
_wb.AllowNavigation = true;
_wb.ScriptErrorsSuppressed = true;
}
public int Attack(int x, int y, ArmyBuilder army)
{
// instruct to attack the village
_action = ENUM.FarmActions.Attack;
//get the army and coordinates
_army = army;
_enemyCoordinates[X] = x;
_enemyCoordinates[Y] = y;
//Place the attack command
_errorFlag = true; // the action is not complated, the flag will set as false once action is complete
_attackFlag = false; // attack is not made yet
_isAlive = true;
Console.WriteLine("-------------------------");
Console.WriteLine("Journey starts");
NavigateThroughTread(_url.GetUrl(ENUM.Screens.RallyPoint));
return _errorFlag ? -1 : CalculateDistance();
}
private void NavigateThroughTread(string url)
{
Console.WriteLine("Defining thread...");
_wb.Navigate(url);
while (_isAlive) Application.DoEvents();
}
private void PageLoaded(object sender, WebBrowserDocumentCompletedEventArgs e)
{
Console.WriteLine("Pages loads...");
.
.
.
switch (_action)
{
.
.
.
case ENUM.FarmActions.Idle:
_wb.Navigate(new Uri("about:blank"));
_action = ENUM.FarmActions.Exit;
return;
case ENUM.FarmActions.Exit:
break;
}
_isAlive = false;
}
This is how I was able to wait without using a thread.
The main problem was probably as you mentioned in number 3 or 5. But I wasn't able to fix the problem as I spent couple of hours.
Anyway thanks for your help it works.

how to count number of visitors for website in asp.net c#

How do I count the number of visitors for website in asp.net c#?
I am using the code below:
In global.asax page:
<%# Application Language="C#" %>
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
Application["NoOfVisitors"] = 0;
}
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Application.Lock();
Application["NoOfVisitors"] = (int)Application["NoOfVisitors"] + 1;
Application.UnLock();
}
In .aspx page:
<asp:Label runat="server" ID="lbluser" />
In .aspx.cs:
protected void Page_Load(object sender, EventArgs e)
{
lbluser.Text = Application["NoOfVisitors"].ToString();
}
The application counter is resetting to 0 every one hour ...
Where have I erred in counting the number of users?
Application State is volatile. Check the this MSDN articule:
When using application state, you must be aware of the following
important considerations:
...
Volatility Because application state is stored in server memory, it
is lost whenever the application is stopped or restarted. For example,
if the Web.config file is changed, the application is restarted and
all application state is lost unless application state values have
been written to a non-volatile storage medium such as a database.
So you should not use that for saving this kind of data that you want to persist over time. Because applications pools get reseted from time to time. And I suspect you don't want to reset your visitor count when that happens.
You'll need some kind of data store which can persist your data when you application is not running.
Here are some choices:
File (XML, JSON, plain text, etc.): sample xml code for visitors counter
Database (SQL Server, SQLite, etc.): sample database code for hit counter
In global.asax file under this method
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Application.Lock();
Application["NoOfVisitors"] = (int)Application["NoOfVisitors"] + 1;
Application.UnLock();
}
then in your page load please add
lblCount.Text = Application["NoOfVisitors"].ToString();
then you can get the number of visitors on your site .
If your application is hosted in IIS and has an application pool, you can check the Application Pool Recycling Settings. Depending on your version, the default is 1740 or 29 hours. Maybe the pool for your application is configured to 60 or around that value? The next setting to check is the Idle Time Out. I believe its default value is 20 on a new server. You can set this to 0. I recommend you read about these settings prior to changing them.
The only possible reason reason could be that, have you hosted your application on a third party server? if yes, it could be that the provider might be killing your application. i have numerous cases where these providers kill your application depending on their memory management schemes.
Simple store the visitor count after changing its value to database and on application start load this value from database that's all you have to do.
You should save count for visits on fly on an xml file under root directory. Check following blog for complete steps :How to count number of visitors in asp.net website
If you want to manage visitor on code level you need to start visitor counter under Application_Start method in application configuration file after need to increase the counter on every session. For more details follow the link given below.
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Application.Lock();
Application["NoOfVisitors"] = (int)Application["NoOfVisitors"] + 1;
Application.UnLock();
}
http://www.freshcodehub.com/Article/49/show-number-of-visitors-in-aspnet-application
Application Pool Restart periodically default settings is 60 minutes.
when app pool restart the count restart as well.

Schedule task in ASP.NET

I'm trying to create a task scheduler that runs twice a day. I've implemented a task scheduler using CacheItemRemovedCallaback, as suggested in this post and this blog. I have the a feature that enables the admin to modified the scheduled times, and saves them on Application variables:
protected void UpdateSchedule(object sender, EventArgs e)
{
Button button = sender as Button;
if (button.ID == "scheduleButton1")
{
Application.Lock();
Application["buildSchedule1"] = GetScheduleTime1;
Application.UnLock();
}
else if (button.ID == "scheduleButton2")
{
Application.Lock();
Application["buildSchedule2"] = GetScheduleTime2;
Application.UnLock();
}
HttpRuntime.Cache.Remove(Global.DummyCachekey); //remove current scheduled task and set new time
}
And on Global.aspx I have:
protected void Application_Start(object sender, EventArgs e)
{
Application.Lock();
Application["buildSchedule1"] = new TimeSpan(10,00, 0); //default time
Application["buildSchedule2"] = new TimeSpan(16,00, 0); //default time
Application.UnLock();
SheduleTask();
};
The problem is that for some reason (probably due to app pool recycling) the schedule times get reset, and even some times the task won't start at the default times.
I found solutions that mention Windows services or Windows task scheduler, but that doesn't work for me since I need to be able to let the admin configure the times through the web application. (I search for this, but couldn't find anything).
I found solutions that mention Windows services or Windows task scheduler
That's what you should be using. A web application doesn't "always run." It responds to requests, that's all. Unless something is actively making a request to the website, it's not doing anything.
but that doesn't work for me since I need to be able to let the admin configure the times through the web application
Sure it does. More than one application can share a single database. In this case you'd have two applications:
A Web Application where users can login and maintain the data related to the scheduled tasks.
A Windows Service (or scheduled Console Application) which runs in the background on the server and executes the configured tasks which it reads from the database.
Using hacks like Windows Scheduled Tasks and Control Panel abilities are not nice solutions. They sucks most of the time, they are a headache.
You can use ATrigger scheduling service. A .Net library is also available to create scheduled tasks without overhead.
//Tags: Tags are required to identify tasks.
//read more at: http://atrigger.com/docs/wiki/9/rest-api-v10-parameter-tag_
Dictionary<string, string> tags = new Dictionary<string, string>();
tags.Add("type", "test");
//Create
ATrigger.Client.doCreate(TimeQuantity.Hour(), "12", "http://www.example.com/myTask?something", tags);
Disclaimer: I was among the ATrigger team. It's a freeware and I have not any commercial purpose.

System.Runtime.Cache not expiring?

I have implemented a solution in my ASP.NET project to automatically send some emails based on a time. I have done this by using the System.Runtime.Cache, specifically the CacheItemRemovedCallback. First of all i add the task to the cache in the Application_Start method:
protected void Application_Start(object sender, EventArgs e)
{
...
AddTask(reportElement.name, totalMinutes);
...
}
and the AddTask method then adds the item to the cache:
private void AddTask(string name, int minutes)
{
OnCacheRemove = new CacheItemRemovedCallback(CacheItemRemoved);
HttpRuntime.Cache.Insert(name, minutes, null, DateTime.Now.AddMinutes(minutes), Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable, OnCacheRemove);
}
So when the cache entry expires after the minutes specified in the AbsolutionExpiration, it calls my CacheItemRemoved method. This basically runs a report, sends an email and then re-adds the task to the cache, so it will run again after the time has expired again - simple. Here is part of the code we are concerned with in the CacheItemRemoved.
public void CacheItemRemoved(string taskName, object minutes, CacheItemRemovedReason r)
{
...
finally
{
AddTask(taskName, Convert.ToInt32(minutes));
}
...
}
There is exception handling in the code, as you can see the re-adding of the task is in the finally block, so should always get called. And all the exception catch blocks do is log the error to the file, as i want to keep the task running even if the previous one fails.
This works perfectly on my local machine, but when on a Windows Server 2003, it basically just runs once. I have added extra debugging and it looks like the second time the cache entry is added, it simply doesn't expire. I am completely stuck now. The windows server is running IIS 6.0. Are there any settings for the cache i don't know about. Also, on the server it seems to expire at a completely different time to what was specified in the minutes.
Thanks in advance.
HttpRuntime.Cache.Insert(name, minutes, null, DateTime.Now.AddMinutes(minutes), Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable, OnCacheRemove);
When you add your cache item why are you specifying CacheItemPriority.NotRemovable, surely this will prevent the item from ever being removed (unless you run out of memory).

Categories

Resources