Short intro:
I have a windows service which monitors other applications and services if they are functional at various time intervals.
The service uses one timer (System.Threading.Timer) for every monitored application (named as "monitor").
Different types of applications require different types of monitors, some work synchronously and others asynchronous (for example, those using HttpClient).
So I got to the point where I would need asynchronous calls in a timer.
I have simplified the code to the limit so that I can post it here. It can be run directly into a console project.
My problem is that this code has a very bizarre behavior, as more timers are introduced - the harder it runs until it does not respond at all (over 20 timers).
Does not the monitor run time be exactly the delay set in asynchronous operation (100ms)?
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace TestMain
{
class TestMain
{
private static List<TestTimer> timers = new List<TestTimer>();
static void Main(string[] args)
{
for (int i = 0; i < 20; i++)
{
TestMain.timers.Add(new TestTimer(i));
}
Console.WriteLine("Press [Enter] to exit.");
Console.ReadLine();
}
public class TestTimer
{
public Int32 Id { get; private set; }
private Timer timer;
public TestTimer(Int32 id)
{
this.Id = id;
this.timer = new Timer(this.Test, null, 1000, 30 * 1000);
}
private void Test(Object state)
{
TestWorker t = new TestWorker(this.Id);
t.Run();
}
}
public class TestWorker
{
public Int32 Id { get; private set; }
private Stopwatch sw = new Stopwatch();
public TestWorker(Int32 id) { this.Id = id; }
public void Run()
{
this.RunAsync().Wait();
}
private async Task RunAsync()
{
this.Log(String.Format("Start[{0,2}]", this.Id));
this.sw.Restart();
await Task.Run(() => { System.Threading.Thread.Sleep(100); }).ConfigureAwait(false);
this.sw.Stop();
this.Log(String.Format(" End[{0,2}] Duration=[{1}]", this.Id, (Int32)this.sw.ElapsedMilliseconds));
}
private void Log(String text)
{
Console.WriteLine(String.Format("{0,20} {1}", DateTime.Now, text));
}
}
}
}
I attached a printscreen with a run.
Console Printscreen
That's because of how thread pool managed its threads. Thread pool has "minimal" number of threads (which you can read with ThreadPool.GetMinThreads). By default (that depends on .NET version but we won't complicate stuff with that) it's related to the number of processor cores, for example on my machine that's 8. When those 8 threads are busy and you need more - thread pool will first wait for some time for one of the busy threads to be available (it will wait for about 1 second) and if no thread is available - it will add one more thread to the pool.
Timer callback executes on thread pool. So when all 20 of your timers fire their callback at the same time - only 8 (in my case) callbacks are executed. The rest are queued and one executes approximately every second (they request thread from thread pool to execute but it waits 1 second every time, because all threads in thread pool are busy at the moment). They are busy because your timer callback waits for RunAsync to complete with Wait(). So only after 12 (20-8) seconds all timer callbacks has executed.
When timer callback executes - it writes Start message to console and starts the Stopwatch. Then you request another thread from thread pool by doing Task.Run. All those requests are queued after timer callbacks, so only after all timers are started you start to receive End messages.
So now you have 20 threads busy with waiting for RunAsync to complete. First Task.Run requests another thread. This thread waits for 100 milliseconds and after that it's free and can be reused, so task pool will not create new threads for each Task.Run and will reuse this one (because 100 milliseconds is less that 1 second it will wait for a thread to become available).
To make this behavior more expected - set minimal threads in thread pool with ThreadPool.SetMinThread to some bigger value or don't hold timer callback thread with waiting for RunAsync to complete.
System.Threading.Timer is using thread pool, so there is a limit on number of threads and this is what you experience.
Does not the monitor run time be exactly the delay set in asynchronous operation (100ms)?
That's what you want, but it seems the thread is busy for a duration of waiting for task to complete and even more, because the task inside also want to use thread from thread pool.
A quick fix is to use fire-and-forget method (credits), this way timer is not waiting for anything, instead of
public void Run()
{
RunAsync().Wait();
}
do
public void Run()
{
#pragma warning disable 4014
RunAsync();
#pragma warning restore 4014
}
Related
I am currently running an async task on polling (meaning this async task is called every 1s). All other items need to update that fast, with exception of my async task below. This code works, but I wonder if it's good practice?
Note: _monitor only gets used by DoAsync()
private readonly object _monitor = new object();
private void PolledEverySecond()
{
_ = DoAsync(); // do this every 5 seconds
// Other stuff
GetNetworkState();
GetCurrentVelocity();
GetCurrentPosition();
Etc;
}
private async Task DoAsync()
{
await Task.Run(() =>
{
if (!Monitor.IsEntered(_monitor))
{
try
{
Monitor.Enter(_monitor);
DoStuff();
}
finally
{
Thread.Sleep(5000);
Monitor.Exit(_monitor);
}
}
});
}
The intention behind the Monitor.Enter/Monitor.Exit and the Thread.Sleep(5000). Is that DoAsync() does not get called every 1 second. I have a polling service that works great to update things and it's used by many of my ViewModels. But, in the case of DoAsync(), it is overkill to poll every second. Therefore, by making it async and using monitors, DoStuff() only gets called approximately every 5-6 seconds.
There are two problems with your current code, a race condition between the Monitor.IsEntered and Monitor.Enter calls, and the blocking of a ThreadPool thread with the Thread.Sleep(5000) call. Blocking ThreadPool threads is not consider a good practice, because it may result to the saturation of the pool. A saturated ThreadPool cannot respond immediately to requests for work, so the program becomes less responsive. Also in this case the ThreadPool has to inject more threads in the pool, resulting to increased memory consumption (each thread requires at least 1 MB of memory).
My suggestion is to switch from the blocking Thread.Sleep to the asynchronous (non-blocking) Task.Delay method, and also to
switch from the thread-affine Monitor to the thread-agnostic SemaphoreSlim. In order to check the availability and acquire the semaphore as an atomic operation, you could use the Wait method, passing zero milliseconds as argument:
private readonly SemaphoreSlim _semaphore = new(1, 1);
private async Task DoAsync()
{
await Task.Run(async () =>
{
bool acquired = _semaphore.Wait(0);
if (acquired)
{
var delayTask = Task.Delay(5000);
try
{
DoStuff();
}
finally
{
await delayTask;
_semaphore.Release();
}
}
});
}
This way the ThreadPool thread will be utilized only during the DoStuff execution, and then it will be freed, and it will be available for other work.
Creating the Task.Delay(5000) task before starting the DoStuff and awaiting it afterwards, has the generally desirable effect of including the duration of the DoStuff into the 5 seconds delay. If you don't want this behavior, you can just create and await the task in the same line: await Task.Delay(5000);
I want to execute methods at different intervals and was looking at using the Timer class to schedule this. However, I wanted to understand if the Timer spun up a new thread for each new schedule which could then potentially impact performance of the application
For System.Threading.Timer:
Short-answer: 90% of the time: no. It uses the Thread Pool to get an existing thread (that isn't doing anything).
Long-answer: Possibly! If all threads in the pool are busy then a new thread will need to be created by the OS and added to the pool and then used by the Timer.
https://learn.microsoft.com/en-us/dotnet/api/system.threading.timer?view=netframework-4.8
Provides a mechanism for executing a method on a thread pool thread at specified intervals. This class cannot be inherited.
For other timer types:
System.Windows.Forms.Timer triggers a new Win32 Window Message to be sent to the Form at a set interval. It does not use a dedicated thread nor a pool thread, instead it uses Win32's SetTimer function.
System.Timers.Timer will also use a pool thread by default (just like System.Threading.Timer) but lets you perform thread synchronization. See https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer?view=netframework-4.8
A single-threaded alternative using coroutines:
I recommend you look at using await Task.Delay instead - as it won't cause a new thread to be used (remember that Tasks are not Threads) - though if you do use Task.Run to run the coroutine in a pool thread then it may run on a new thread:
public static class Foo
{
public static async Task<Int32> Main( String[] args )
{
Task loop30 = this.Every30Seconds();
Task loop20 = this.Every20Seconds();
Taks loop10 = this.Every10Seconds();
await Task.WhenAll( loop30, loop20, loop10 );
return 0;
}
public static async Task Every30Seconds()
{
while( true )
{
Console.WriteLine("Ping!");
await Task.Delay( 30 * 1000 );
}
}
public static async Task Every20Seconds()
{
while( true )
{
Console.WriteLine("Pong!");
await Task.Delay( 20 * 1000 );
}
}
public static async Task Every10Seconds()
{
while( true )
{
Console.WriteLine("Pang!");
await Task.Delay( 10 * 1000 );
}
}
}
Dai's done a good job of answering the question regarding timers and threads.
I thought I'd give you an alternative way of writing your code. You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq; - then you can do this:
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
EventLoopScheduler els = new EventLoopScheduler();
els.Schedule(() => Console.WriteLine(Thread.CurrentThread.ManagedThreadId));
IObservable<string> pings = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(30), els).Select(x => "Ping!");
IObservable<string> pongs = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(20), els).Select(x => "Pong!");
IObservable<string> pangs = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(10), els).Select(x => "Pang!");
IObservable<string> query = Observable.Merge(els, pings, pongs, pangs);
IDisposable subscription = query.Subscribe(x => Console.WriteLine($"{x} ({Thread.CurrentThread.ManagedThreadId})"));
Console.ReadLine();
subscription.Dispose();
els.Dispose();
The EventLoopScheduler creates a single, dedicated, re-usable thread that you can used until you call .Dispose() on it.
Both Observable.Timer and Observable.Merge allow you to specify that you want to use the EventLoopScheduler to ensure that the code is run on that thread.
On a console application, i am currently starting an array of threads. The thread is passed an object and running a method in it. I would like to know how to call a method on the object inside the individual running threads.
Dispatcher doesn't work. SynchronizationContext "Send" runs on the calling thread and "Post" uses a new thread. I would like to be able to call the method and pass parameters on a running thread on the target thread it's running on and not the calling thread.
Update 2: Sample code
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CallingFromAnotherThread
{
class Program
{
static void Main(string[] args)
{
var threadCount = 10;
var threads = new Thread[threadCount];
Console.WriteLine("Main on Thread " + Thread.CurrentThread.ManagedThreadId);
for (int i = 0; i < threadCount; i++)
{
Dog d = new Dog();
threads[i] = new Thread(d.Run);
threads[i].Start();
}
Thread.Sleep(5000);
//how can i call dog.Bark("woof");
//on the individual dogs and make sure they run on the thread they were created on.
//not on the calling thread and not on a new thread.
}
}
class Dog
{
public void Run()
{
Console.WriteLine("Running on Thread " + Thread.CurrentThread.ManagedThreadId);
}
public void Bark(string text)
{
Console.WriteLine(text);
Console.WriteLine("Barking on Thread " + Thread.CurrentThread.ManagedThreadId);
}
}
}
Update 1:
Using synchronizationContext.Send results to using the calling thread
Channel created
Main thread 10
SyncData Added for thread 11
Consuming channel ran on thread 11
Calling AddConsumer on thread 10
Consumer added consumercb78b. Executed on thread 10
Calling AddConsumer on thread 10
Consumer added consumer783c4. Executed on thread 10
Using synchronizationContext.Post results to using a different thread
Channel created
Main thread 10
SyncData Added for thread 11
Consuming channel ran on thread 11
Calling AddConsumer on thread 12
Consumer added consumercb78b. Executed on thread 6
Calling AddConsumer on thread 10
Consumer added consumer783c4. Executed on thread 7
The target thread must run the code "on itself" - or it is just accessing the object across threads. This is done with some form of event dispatch loop on the target thread itself.
The SynchronizationContext abstraction can and does support this if the underlying provider supports it. For example in either WinForms or WPF (which themselves use the "window message pump") using Post will "run on the UI thread".
Basically, all such constructs follow some variation of the pattern:
// On "target thread"
while (running) {
var action = getNextDelegateFromQueue();
action();
}
// On other thread
postDelegateToQueue(actionToDoOnTargetThread);
It is fairly simple to create a primitive queue system manually - just make sure to use the correct synchronization guards. (Although I am sure there are tidy "solved problem" libraries out there; including wrapping everything up into a SynchronizationContext.)
Here is a primitive version of the manual queue. Note that there may be is1 a race condition.. but, FWIW:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace DogPark
{
internal class DogPark
{
private readonly string _parkName;
private readonly Thread _thread;
private readonly ConcurrentQueue<Action> _actions = new ConcurrentQueue<Action>();
private volatile bool _isOpen;
public DogPark(string parkName)
{
_parkName = parkName;
_isOpen = true;
_thread = new Thread(OpenPark);
_thread.Name = parkName;
_thread.Start();
}
// Runs in "target" thread
private void OpenPark(object obj)
{
while (true)
{
Action action;
if (_actions.TryDequeue(out action))
{
Program.WriteLine("Something is happening at {0}!", _parkName);
try
{
action();
}
catch (Exception ex)
{
Program.WriteLine("Bad dog did {0}!", ex.Message);
}
}
else
{
// Nothing left!
if (!_isOpen && _actions.IsEmpty)
{
return;
}
}
Thread.Sleep(0); // Don't toaster CPU
}
}
// Called from external thread
public void DoItInThePark(Action action)
{
if (_isOpen)
{
_actions.Enqueue(action);
}
}
// Called from external thread
public void ClosePark()
{
_isOpen = false;
Program.WriteLine("{0} is closing for the day!", _parkName);
// Block until queue empty.
while (!_actions.IsEmpty)
{
Program.WriteLine("Waiting for the dogs to finish at {0}, {1} actions left!", _parkName, _actions.Count);
Thread.Sleep(0); // Don't toaster CPU
}
Program.WriteLine("{0} is closed!", _parkName);
}
}
internal class Dog
{
private readonly string _name;
public Dog(string name)
{
_name = name;
}
public void Run()
{
Program.WriteLine("{0} is running at {1}!", _name, Thread.CurrentThread.Name);
}
public void Bark()
{
Program.WriteLine("{0} is barking at {1}!", _name, Thread.CurrentThread.Name);
}
}
internal class Program
{
// "Thread Safe WriteLine"
public static void WriteLine(params string[] arguments)
{
lock (Console.Out)
{
Console.Out.WriteLine(arguments);
}
}
private static void Main(string[] args)
{
Thread.CurrentThread.Name = "Home";
var yorkshire = new DogPark("Yorkshire");
var thunderpass = new DogPark("Thunder Pass");
var bill = new Dog("Bill the Terrier");
var rosemary = new Dog("Rosie");
bill.Run();
yorkshire.DoItInThePark(rosemary.Run);
yorkshire.DoItInThePark(rosemary.Bark);
thunderpass.DoItInThePark(bill.Bark);
yorkshire.DoItInThePark(rosemary.Run);
thunderpass.ClosePark();
yorkshire.ClosePark();
}
}
}
The output should look about like the following - keep in mind that this will change when run multiples times due to the inherent nature of non-synchronized threads.
Bill the Terrier is running at Home!
Something is happening at Thunder Pass!
Something is happening at Yorkshire!
Rosie is running at Yorkshire!
Bill the Terrier is barking at Thunder Pass!
Something is happening at Yorkshire!
Rosie is barking at Yorkshire!
Something is happening at Yorkshire!
Rosie is running at Yorkshire!
Thunder Pass is closing for the day!
Thunder Pass is closed!
Yorkshire is closing for the day!
Yorkshire is closed!
There is nothing preventing a dog from performing at multiple dog parks simultaneously.
1 There is a race condition present and it is this: a park may close before the last dog action runs.
This is because the dog park thread dequeues the action before the action is run - and the method to close the dog park only waits until all the actions are dequeued.
There are multiple ways to address it, for instance:
The concurrent queue could first peek-use-then-dequeue-after-the-action, or
A separate volatile isClosed-for-real flag (set from the dog park thread) could be used, or ..
I've left the bug in as a reminder of the perils of threading..
A running thread is already executing a method. You cannot directly force that thread to leave the method and enter a new one. However, you could send information to that thread to leave the current method and do something else. But this only works if the executed method can react on that passed information.
In general, you can use threads to call/execute methods, but you cannot call a method ON a running thread.
Edit, based on your updates:
If you want to use the same threads to execute dog.run and dog.bark, and do it in the same objects, the you need to modify your code:
static void Main(string[] args)
{
var threadCount = 10;
var threads = new Thread[threadCount];
Console.WriteLine("Main on Thread " + Thread.CurrentThread.ManagedThreadId);
// keep the dog objects outside the creation block in order to access them later again. Always useful.
Dog[] dogs = New Dog[threadCount];
for (int i = 0; i < threadCount; i++)
{
dogs[i] = new Dog();
threads[i] = new Thread(d.Run);
threads[i].Start();
}
Thread.Sleep(5000);
//how can i call dog.Bark("woof") --> here you go:
for (int i = 0; i < threadCount; i++)
{
threads[i] = new Thread(d.Bark);
threads[i].Start();
}
// but this will create NEW threads because the others habe exited after finishing d.run, and habe been deleted. Is this a problem for you?
// maybe the other threads are still running, causing a parallel execution of d.run and d.bark.
//on the individual dogs and make sure they run on the thread they were created on.
//not on the calling thread and not on a new thread. -->
// instead of d.run, call d.doActions and loop inside that function, check for commands from external sources:
for (int i = 0; i < threadCount; i++)
{
threads[i] = new Thread(d.doActions);
threads[i].Start();
}
// but in this case there will be sequential execution of actions. No parallel run and bark.
}
Inside your dog class:
Enum class EnumAction
{
Nothing,
Run,
bark,
exit,
};
EnumAction m_enAction;
Object m_oLockAction;
void SetAction (EnumAction i_enAction)
{
Monitor.Enter (m_oLockAction);
m_enAction = i_enAction;
Monitor.Exit (m_oLockAction);
}
void SetAction (EnumAction i_enAction)
{
Monitor.Enter (m_oLockAction);
m_enAction = i_enAction;
Monitor.Exit (m_oLockAction);
}
Void doActions()
{
EnumAction enAction;
Do
{
Thread.sleep(20);
enAction = GetAction();
Switch(enAction)
{
Case EnumAction.run:
Run(); break;
Case ...
}
} while (enAction != EnumAction.exit);
}
Got it? ;-)
Sorry for any typos, I was typing on my mobile phone, and I usually use C++CLI.
Another advice: as you would read the variable m_enAction inside the thread and write it from outside, you need to ensure that it gets updated properly due to the access from different threads. The threads MUST NOT cache the variable in the CPU, otherwise they don't see it changing. Use locks (e.g. Monitor) to achieve that. (But do not use a Monitor on m_enAction, because you can use Monitors only on objects. Create a dummy object for this purpose.)
I have added the necessary code. Check out the differences between the edits to see the changes.
You cannot run second method while first method is running. If you want them to run in parallel you need another thread. However, your object needs to be thread safe.
Execution of thread simply means execution of sequence of instruction. Dispatcher is nothing else than an infinite loop that executes queued method one after another.
I recommend you to use tasks instead of threads. Use Parallel.ForEach to run Dog.Run method on each dog object instance. To run Bark method use Task.Run(dog.Bark).
Since you used running and barking dog as an example you could write your own "dispatcher". That means infinite loop that would execute all queued work. In that case you could have all dogs in single thread. Sounds weird, but you could have unlimited amount of dogs. At the end, only as many threads can be executed at the same time as many CPU cores is available
I'm writing a Windows Service that will kick off multiple worker threads that will listen to Amazon SQS queues and process messages. There will be about 20 threads listening to 10 queues.
The threads will have to be always running and that's why I'm leaning towards to actually using actual threads for the worker loops rather than threadpool threads.
Here is a top level implementation. Windows service will kick off multiple worker threads and each will listen to it's queue and process messages.
protected override void OnStart(string[] args)
{
for (int i = 0; i < _workers; i++)
{
new Thread(RunWorker).Start();
}
}
Here is the implementation of the work
public async void RunWorker()
{
while(true)
{
// .. get message from amazon sqs sync.. about 20ms
var message = sqsClient.ReceiveMessage();
try
{
await PerformWebRequestAsync(message);
await InsertIntoDbAsync(message);
}
catch(SomeExeception)
{
// ... log
//continue to retry
continue;
}
sqsClient.DeleteMessage();
}
}
I know I can perform the same operation with Task.Run and execute it on the threadpool thread rather than starting individual thread, but I don't see a reason for that since each thread will always be running.
Do you see any problems with this implementation? How reliable would it be to leave threads always running in this fashion and what can I do to make sure that each thread is always running?
One problem with your existing solution is that you call your RunWorker in a fire-and-forget manner, albeit on a new thread (i.e., new Thread(RunWorker).Start()).
RunWorker is an async method, it will return to the caller when the execution point hits the first await (i.e. await PerformWebRequestAsync(message)). If PerformWebRequestAsync returns a pending task, RunWorker returns and the new thread you just started terminates.
I don't think you need a new thread here at all, just use AmazonSQSClient.ReceiveMessageAsync and await its result. Another thing is that you shouldn't be using async void methods unless you really don't care about tracking the state of the asynchronous task. Use async Task instead.
Your code might look like this:
List<Task> _workers = new List<Task>();
CancellationTokenSource _cts = new CancellationTokenSource();
protected override void OnStart(string[] args)
{
for (int i = 0; i < _MAX_WORKERS; i++)
{
_workers.Add(RunWorkerAsync(_cts.Token));
}
}
public async Task RunWorkerAsync(CancellationToken token)
{
while(true)
{
token.ThrowIfCancellationRequested();
// .. get message from amazon sqs sync.. about 20ms
var message = await sqsClient.ReceiveMessageAsync().ConfigureAwait(false);
try
{
await PerformWebRequestAsync(message);
await InsertIntoDbAsync(message);
}
catch(SomeExeception)
{
// ... log
//continue to retry
continue;
}
sqsClient.DeleteMessage();
}
}
Now, to stop all pending workers, you could simple do this (from the main "request dispatcher" thread):
_cts.Cancel();
try
{
Task.WaitAll(_workers.ToArray());
}
catch (AggregateException ex)
{
ex.Handle(inner => inner is OperationCanceledException);
}
Note, ConfigureAwait(false) is optional for Windows Service, because there's no synchronization context on the initial thread, by default. However, I'd keep it that way to make the code independent of the execution environment (for cases where there is synchronization context).
Finally, if for some reason you cannot use ReceiveMessageAsync, or you need to call another blocking API, or simply do a piece of CPU intensive work at the beginning of RunWorkerAsync, just wrap it with Task.Run (as opposed to wrapping the whole RunWorkerAsync):
var message = await Task.Run(
() => sqsClient.ReceiveMessage()).ConfigureAwait(false);
Well, for one I'd use a CancellationTokenSource instantiated in the service and passed down to the workers. Your while statement would become:
while(!cancellationTokenSource.IsCancellationRequested)
{
//rest of the code
}
This way you can cancel all your workers from the OnStop service method.
Additionally, you should watch for:
If you're playing with thread states from outside of the thread, then a ThreadStateException, or ThreadInterruptedException or one of the others might be thrown. So, you want to handle a proper thread restart.
Do the workers need to run without pause in-between iterations? I would throw in a sleep in there (even a few ms's) just so they don't keep the CPU up for nothing.
You need to handle ThreadStartException and restart the worker, if it occurs.
Other than that there's no reason why those 10 treads can't run for as long as the service runs (days, weeks, months at a time).
I have a WCF app that accepts requests to start a job. Each job needs to do something after exactly X minutes (e.g. 5 mins.), there can also be a job request at any time and simultaneously.
This is what I have in mind,
// WCF class
public class RequestManager
{
// WCF method
public void StartNewJob()
{
// start a new thread with timer for each job?
}
}
public class Job
{
public Job()
{
// do some initializations
// do something after x mins
// sleep or timer?
}
private void DoSomething()
{
// do some follow-ups
}
}
With my approach, I'm afraid that there will be too many threads that's doing nothing for X mins. Per-second accuracy would be a requirement as well (say it starts a job at 0:05:01, the follow up should be at 0:10:01).
What would be the best way to approach this?
I would suggest you looking at the RegisterWaitForSingleObject function:
var waitObject = new AutoResetEvent(false);
// Execute the callback on a new thread 10 seconds after this call
// and execute it only once
ThreadPool.RegisterWaitForSingleObject(
waitObject,
(state, timeout) => { Console.WriteLine("ok"); },
null,
TimeSpan.FromSeconds(10),
true);
// Execute the callback on a new thread 10 seconds after this call
// and continue executing it at 10 seconds intervals until the
// waitHandle is signaled.
ThreadPool.RegisterWaitForSingleObject(
waitObject,
(state, timeout) => { Console.WriteLine("ok"); },
null,
TimeSpan.FromSeconds(10),
false);
Sounds like you need the serives of the Timer class:
// WCF class
public class RequestManager
{
// WCF method
public void StartNewJob()
{
Job myJob = new Job();
// Initialise myJob...
myJob.Start();
}
}
public class Job
{
private Timer myTimer = new Timer();
public Job()
{
myTimer.Elapsed += new ElapsedEventHandler(this.OnTimedEvent);
}
public void Start(int Miniutes)
{
myTimer.Interval = 60000 * Miniutes;
myTimer.Enabled = true;
}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
// So something
}
}
The above code assumes that:
You dont do anything silly like attempt to call Start() twice on the same instance of timer.
There is some other non-background thread active elsewhere in the application preventing the application from closing.
Its not a full example, but hopefully it should give you the idea - the Timer class will deal with keeping time without needing a thread active for each job.
You need to use some timing/scheduling framework like Quartz.NET or create your own one (lightweight).
Using timer seems to be good (and easier to implement) for me.
There are several timer classes you can use in .NET. Please see the following document (even though it's bit aged, but it seems to be a good start): Comparing the Timer Classes in the .NET Framework Class Library
However, you can still achieve this behavior with Thread.Sleep() as well by calculating the offset while taking timestamps on a thread wake-up and on a completion of Job.DoSomethig().
You may want to consider the followings carefully:
Any contentions between threads executing Job.DoSomething()?
You should be very careful in the following scenario: what if Job.DoSomething() sometimes takes more than the period (i.e. it starts at 0:05 and completes 0:13 in the example above). What does this mean to your application and how will it be handled?
a. Total failure - abort the current(0:05) execution at 0:10 and launch 0:10 execution.
b. Not a big deal (skip 0:10 one and run Job.DoSomething() at 0:15).
c. Not a big deal, but need to launch 0:10 execution immediately after 0:05 task finishes (what if it keeps taking more than 5 sec??).
d. Need to launch 0:10 execution even though 0:05 execution is currently running.
e. anything else?
For the policy you select above, does your choice of implementation (either any of timer classes listed above or Thread.Sleep()) easy to support your policy?