One way to do it, if you do not need to do it on a scheduled time, but just needs to clean up "once in a while", is to create a function in your Global.asax Session_OnEnd() that will create a random number between 1 and 100, and if the number is say, 50, you execute the maintenance task.
Offcourse you can reduce the "100" to have the task happen more frequently.
Also there is and article titled 'Simulate a Windows Service using ASP.NET to run scheduled jobs' at http://www.codeproject.com/aspnet/ASPNETService.asp that uses an expiring cache to simulate a timer. It claims it can be run on any hosted site.
If you are using the last one, please read this comment from a post about this technique:
You need to really be careful on the
length of the task running. Every new
Task is a new Worker Thread and
there’s a limited number of those - as
it “borrows” a thread from the managed
thread pool.
Starting in v3.5 of the Framework the
maximum number of threads was
increased 10x from 25 to 250. But
there’s now a logarithmic startup to
them, so as it doles out more threads
it gets stingier with them. If you run
out of available threads in the
managed thread pool - your response
times are going to go through the
roof.
What you’re really writing here is a
messaging/queuing system.
If you’re doing things like updating
the cache, then by all means - kick
off a new task. If you’re doing
something like downloading a secondary
HTTP resource or some kind of
intensive database work - write a
Windows Service and use a Queue that
allows you more control over how much
you “bite” off each time.