Multithreading in WCF with accurate timer requirement - c#

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?

Related

Strange behaviour with async operations inside System.Threading.Timer

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
}

multithreaded unit test - notify calling thread of event to stop sleeping

I wrote a test (NUnit) to test my Amazon SES email sending functionality which should generate a bounce notification in my Amazon SQS queue. I poll this queue in a loop for 1 minute on a new thread to wait and verify the bounce notification.
I'd like to increase the time to a few minutes to ensure I dont miss it. However the response may come within seconds in which case I'd want to just record how long it took and finish the test as there is no point to continue waiting once receipt is verified.
How can I accomplish this threading scenario in a clean way, and by that I mean without polluting MethodInMainApp() with test code. In the main app this should not happen (it should continue polling indefinitely), it should only stop early in test. I should probably pass in the ThreadStart function from both entry points but that doesnt answer the question Im asking.
[Test]
public async void SendAndLogBounceEmailNotification()
{
Thread bouncesThread = Startup.MethodInMainApp();
bouncesThread.Start();
bool success = await _emailService.SendAsync(...);
Assert.AreEqual(success, true);
//Sleep this thread for 1 minute while the
//bouncesThread polls for bounce notifications
Thread.Sleep(60000);
}
public static Thread MethodInMainApp()
{
...
Thread bouncesThread = new Thread(() =>
{
while (true)
{
ReceiveMessageResponse receiveMessageResponse = sqsBouncesClient.ReceiveMessage(bouncesQueueRequest);
if(receiveMessageResponse.Messages.Count > 0)
{
ProcessQueuedBounce(receiveMessageResponse);
//done for test
}
}
});
bouncesThread.SetApartmentState(ApartmentState.STA);
return bouncesThread;
}
Use the Monitor class.
Instead of Thread.Sleep(60000), use this:
lock (_lockObject)
{
Monitor.Wait(_lockObject, 60000);
}
Then where you want to signal the thread to continue:
lock (_lockObject)
{
Monitor.Pulse(_lockObject);
}
Of course this requires adding static readonly object _lockObject = new object() somewhere in the class.
I'm not saying that the overall strategy is actually the right approach. Seems like here, it would be better for the unit test method to explicitly call some other method which is doing the work to wait for and validate the response. But the above should address your specific question.

Non blocking and reoccurring producer/consumer notifier implementation

Searched hard for a piece of code which does what i want and i am happy with. Reading this and this helped a lot.
I have a scenario where i need a single consumer to be notified by a single producer when new data is available but would also like the consumer to be notified periodically regardless of if new data is available.
It is fine if the consumer is notified more than the reoccurring period but it should not be notified less frequent.
It is possible that multiple notifications for 'new data' occur while the consumer is already notified and working. (So SemaphoreSlim was not a good fit).
Hence, a consumer which is slower than the rate of producer notifications, would not queue up subsequent notifications, they would just "re-signal" that same "data available" flag without affect.
I would also like the consumer to asynchronously wait for the notifications (without blocking a thread).
I have stitched together the below class which wraps around TaskCompletionSource and also uses an internal Timer.
public class PeriodicalNotifier : IDisposable
{
// Need some dummy type since TaskCompletionSource has only the generic version
internal struct VoidTypeStruct { }
// Always reuse this allocation
private static VoidTypeStruct dummyStruct;
private TaskCompletionSource<VoidTypeStruct> internalCompletionSource;
private Timer reSendTimer;
public PeriodicalNotifier(int autoNotifyIntervalMs)
{
internalCompletionSource = new TaskCompletionSource<VoidTypeStruct>();
reSendTimer = new Timer(_ => Notify(), null, 0, autoNotifyIntervalMs);
}
public async Task WaitForNotifictionAsync(CancellationToken cancellationToken)
{
using (cancellationToken.Register(() => internalCompletionSource.TrySetCanceled()))
{
await internalCompletionSource.Task;
// Recreate - to be able to set again upon the next wait
internalCompletionSource = new TaskCompletionSource<VoidTypeStruct>();
}
}
public void Notify()
{
internalCompletionSource.TrySetResult(dummyStruct);
}
public void Dispose()
{
reSendTimer.Dispose();
internalCompletionSource.TrySetCanceled();
}
}
Users of this class can do something like this:
private PeriodicalNotifier notifier = new PeriodicalNotifier(100);
// ... In some task - which should be non-blocking
while (some condition)
{
await notifier.WaitForNotifictionAsync(_tokenSource.Token);
// Do some work...
}
// ... In some thread, producer added new data
notifier.Notify();
Efficiency is important to me, the scenario is of a high frequency data stream, and so i had in mind:
The non-blocking nature of the wait.
I assume Timer is more efficient than recreating Task.Delay and cancelling it if it's not the one to notify.
A concern for the recreation of the TaskCompletionSource
My questions are:
Does my code correctly solve the problem? Any hidden pitfalls?
Am i missing some trivial solution / existing block for this use case?
Update:
I have reached a conclusion that aside from re implementing a more lean Task Completion structure (like in here and here) i have no more optimizations to make. Hope that helps anyone looking at a similar scenario.
Yes, your implementation makes sense but the TaskCompletionSource recreation should be outside the using scope, otherwise the "old" cancellation token may cancel the "new" TaskCompletionSource.
I think using some kind of AsyncManualResetEvent combined with a Timer would be simpler and less error-prone. There's a very nice namespace with async tools in the Visual Studio SDK by Microsoft. You need to install the SDK and then reference the Microsoft.VisualStudio.Threading assembly. Here's an implementation using their AsyncManualResetEvent with the same API:
public class PeriodicalNotifier : IDisposable
{
private readonly Timer _timer;
private readonly AsyncManualResetEvent _asyncManualResetEvent;
public PeriodicalNotifier(TimeSpan autoNotifyInterval)
{
_asyncManualResetEvent = new AsyncManualResetEvent();
_timer = new Timer(_ => Notify(), null, TimeSpan.Zero, autoNotifyInterval);
}
public async Task WaitForNotifictionAsync(CancellationToken cancellationToken)
{
await _asyncManualResetEvent.WaitAsync().WithCancellation(cancellationToken);
_asyncManualResetEvent.Reset();
}
public void Notify()
{
_asyncManualResetEvent.Set();
}
public void Dispose()
{
_timer.Dispose();
}
}
You notify by setting the reset event, asynchronously wait using WaitAsync, enable Cancellation using the WithCancellation extension method and then reset the event. Multiple notifications are "merged" by setting the same reset event.
Subject<Result> notifier = new Subject<Result)();
notifier
.Select(value => Observable.Interval(TimeSpan.FromMilliSeconds(100))
.Select(_ => value)).Switch()
.Subscribe(value => DoSomething(value));
//Some other thread...
notifier.OnNext(...);
This Rx query will keep sending value, every 100 milliseconds, until a new value turns up. Then we notify that value every 100 milliseconds.
If we receive values faster than once every 100 milliseconds, then we basically have the same output as input.

How to limit the execution time of a function in c sharp?

I've got a problem. I'm writing a benchmark and I have a function than is either done in 2 seconds or after ~5 minutes(depending on the input data). And I would like to stop that function if it's executed for more than 3 seconds...
How can I do it?
Thanks a lot!
Well..., I had the same question, and after reading all the answers here and the referred blogs, I settled for this,
It Lets me execute any block of code with a time limit, Declare the wrapper method
public static bool ExecuteWithTimeLimit(TimeSpan timeSpan, Action codeBlock)
{
try
{
Task task = Task.Factory.StartNew(() => codeBlock());
task.Wait(timeSpan);
return task.IsCompleted;
}
catch (AggregateException ae)
{
throw ae.InnerExceptions[0];
}
}
And use that to wrap any block of code like this
// code here
bool Completed = ExecuteWithTimeLimit(TimeSpan.FromMilliseconds(1000), () =>
{
//
// Write your time bounded code here
//
});
//More code
The best way would be that your function can check its execution time often enough to decide to stop it it takes too long.
If this is not the case, then run the function in a separate thread. In your main thread start a 3 seconds timer. When timer elapses, kill the separate thread using Thread.Abort() (of course unless the function is already over). See sample code and preacuations of usage in the function docs.
The best way in C# to stop function in middle is the return keyword in function, but how do I know when to use the return keyword to stop the function in middle, after it lasts at least 3 seconds? The Stopwatch class from System.Diagnostics is the answer. This complicated function that lasts between 2 seconds to 5 minutes (depending on the input data) logically uses many loops, and maybe even recursion, so my solution for you is that, at the first line code of that function, create an instance of Stopwatch using System.Diagnostics with the new keyword, start it by calling the Start() function of the Stopwatch class, and in for each loop and loop, at the beginning, add the following code:
if (stopwatch.ElapsedMilliseconds >= 3000) {
stopwatch.Stop();
// or
stopwatch.Reset();
return;
}
(tip: you can type it with hands once, copy it Ctrl+C, and then just paste it Ctrl+V). If that function uses recursion, in order to save memory, make the Stopwatch global instance rather than creating it as local instance at first, and start it if it does not running at the beginning of the code. You can know that with the IsRunning of the Stopwatch class. After that ask if elapsed time is more than 3 seconds, and if yes (true) stop or reset the Stopwatch, and use the return keyword to stop the recursion loop, very good start in function, if your function lasts long time due mainly recursion more than loops. That it is. As you can see, it is very simple, and I tested this solution, and the results showed that it works! Try it yourself!
private static int LongRunningMethod()
{
var r = new Random();
var randomNumber = r.Next(1, 10);
var delayInMilliseconds = randomNumber * 1000;
Task.Delay(delayInMilliseconds).Wait();
return randomNumber;
}
And
var task = Task.Run(() =>
{
return LongRunningMethod();
});
bool isCompletedSuccessfully = task.Wait(TimeSpan.FromMilliseconds(3000));
if (isCompletedSuccessfully)
{
return task.Result;
}
else
{
throw new TimeoutException("The function has taken longer than the maximum time allowed.");
}
it work for me!
Source: https://jeremylindsayni.wordpress.com/2016/05/28/how-to-set-a-maximum-time-to-allow-a-c-function-to-run-for/
You can use the fork/join pattern, in the Task Parallel Library this is implemented with Task.WaitAll()
using System.Threading.Tasks;
void CutoffAfterThreeSeconds() {
// start function on seperate thread
CancellationTokenSource cts = new CancellationTokenSource();
Task loop = Task.Factory.StartNew(() => Loop(cts.Token));
// wait for max 3 seconds
if(Task.WaitAll(new Task[]{loop}, 3000)){
// Loop finished withion 3 seconds
} else {
// it did not finish within 3 seconds
cts.Cancel();
}
}
// this one takes forever
void Loop() {
while (!ct.IsCancellationRequested) {
// your loop goes here
}
Console.WriteLine("Got Cancelled");
}
This will start the other task on a seperate thread, and then wait for 3000 milliseconds for it to finish. If it did finish within the timeout, it return true, else false so you can use that to decide what to do next.
You can use a CancellationToken to communicate to the other thread that it result is no longer needed so it can stop gracefully.
Regards Gert-Jan
Run this function in thread and kill it after 3 seconds or check elapsed time inside this function(I think it's loop there).
Use an OS callbacks with a hi performance counter, then kill your thread, if exists
It is possible to execute a function in a separate thread and limit its execution with Thread.Join(millisecondsTimeout):
using System.Threading;
Thread workThread = new Thread(DoFunc);
workThread.Start(param);
if (!workThread.Join(3000))
{
// DoFunc() took longer than 3 seconds. Thread was aborted
}
private void DoFunc(object param)
{
// do some long work
}
Since C# and .net framework are not real-time environments, you can't guarantee even the 3 seconds count. Even if you were to get close to that, you would still have to call the
if(timeSpan > TimeSpan.FromSeconds(3) then goto endindentifier; before every other call in the method.
All this is just wrong so no, there is just no reliable way to do it from what I know.
Although you can try this solution
https://web.archive.org/web/20140222210133/http://kossovsky.net/index.php/2009/07/csharp-how-to-limit-method-execution-time
but I just wouldn't do such things in .net application.

How to effectively log asynchronously?

I am using Enterprise Library 4 on one of my projects for logging (and other purposes). I've noticed that there is some cost to the logging that I am doing that I can mitigate by doing the logging on a separate thread.
The way I am doing this now is that I create a LogEntry object and then I call BeginInvoke on a delegate that calls Logger.Write.
new Action<LogEntry>(Logger.Write).BeginInvoke(le, null, null);
What I'd really like to do is add the log message to a queue and then have a single thread pulling LogEntry instances off the queue and performing the log operation. The benefit of this would be that logging is not interfering with the executing operation and not every logging operation results in a job getting thrown on the thread pool.
How can I create a shared queue that supports many writers and one reader in a thread safe way? Some examples of a queue implementation that is designed to support many writers (without causing synchronization/blocking) and a single reader would be really appreciated.
Recommendation regarding alternative approaches would also be appreciated, I am not interested in changing logging frameworks though.
I wrote this code a while back, feel free to use it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace MediaBrowser.Library.Logging {
public abstract class ThreadedLogger : LoggerBase {
Queue<Action> queue = new Queue<Action>();
AutoResetEvent hasNewItems = new AutoResetEvent(false);
volatile bool waiting = false;
public ThreadedLogger() : base() {
Thread loggingThread = new Thread(new ThreadStart(ProcessQueue));
loggingThread.IsBackground = true;
loggingThread.Start();
}
void ProcessQueue() {
while (true) {
waiting = true;
hasNewItems.WaitOne(10000,true);
waiting = false;
Queue<Action> queueCopy;
lock (queue) {
queueCopy = new Queue<Action>(queue);
queue.Clear();
}
foreach (var log in queueCopy) {
log();
}
}
}
public override void LogMessage(LogRow row) {
lock (queue) {
queue.Enqueue(() => AsyncLogMessage(row));
}
hasNewItems.Set();
}
protected abstract void AsyncLogMessage(LogRow row);
public override void Flush() {
while (!waiting) {
Thread.Sleep(1);
}
}
}
}
Some advantages:
It keeps the background logger alive, so it does not need to spin up and spin down threads.
It uses a single thread to service the queue, which means there will never be a situation where 100 threads are servicing the queue.
It copies the queues to ensure the queue is not blocked while the log operation is performed
It uses an AutoResetEvent to ensure the bg thread is in a wait state
It is, IMHO, very easy to follow
Here is a slightly improved version, keep in mind I performed very little testing on it, but it does address a few minor issues.
public abstract class ThreadedLogger : IDisposable {
Queue<Action> queue = new Queue<Action>();
ManualResetEvent hasNewItems = new ManualResetEvent(false);
ManualResetEvent terminate = new ManualResetEvent(false);
ManualResetEvent waiting = new ManualResetEvent(false);
Thread loggingThread;
public ThreadedLogger() {
loggingThread = new Thread(new ThreadStart(ProcessQueue));
loggingThread.IsBackground = true;
// this is performed from a bg thread, to ensure the queue is serviced from a single thread
loggingThread.Start();
}
void ProcessQueue() {
while (true) {
waiting.Set();
int i = ManualResetEvent.WaitAny(new WaitHandle[] { hasNewItems, terminate });
// terminate was signaled
if (i == 1) return;
hasNewItems.Reset();
waiting.Reset();
Queue<Action> queueCopy;
lock (queue) {
queueCopy = new Queue<Action>(queue);
queue.Clear();
}
foreach (var log in queueCopy) {
log();
}
}
}
public void LogMessage(LogRow row) {
lock (queue) {
queue.Enqueue(() => AsyncLogMessage(row));
}
hasNewItems.Set();
}
protected abstract void AsyncLogMessage(LogRow row);
public void Flush() {
waiting.WaitOne();
}
public void Dispose() {
terminate.Set();
loggingThread.Join();
}
}
Advantages over the original:
It's disposable, so you can get rid of the async logger
The flush semantics are improved
It will respond slightly better to a burst followed by silence
Yes, you need a producer/consumer queue. I have one example of this in my threading tutorial - if you look my "deadlocks / monitor methods" page you'll find the code in the second half.
There are plenty of other examples online, of course - and .NET 4.0 will ship with one in the framework too (rather more fully featured than mine!). In .NET 4.0 you'd probably wrap a ConcurrentQueue<T> in a BlockingCollection<T>.
The version on that page is non-generic (it was written a long time ago) but you'd probably want to make it generic - it would be trivial to do.
You would call Produce from each "normal" thread, and Consume from one thread, just looping round and logging whatever it consumes. It's probably easiest just to make the consumer thread a background thread, so you don't need to worry about "stopping" the queue when your app exits. That does mean there's a remote possibility of missing the final log entry though (if it's half way through writing it when the app exits) - or even more if you're producing faster than it can consume/log.
Here is what I came up with... also see Sam Saffron's answer. This answer is community wiki in case there are any problems that people see in the code and want to update.
/// <summary>
/// A singleton queue that manages writing log entries to the different logging sources (Enterprise Library Logging) off the executing thread.
/// This queue ensures that log entries are written in the order that they were executed and that logging is only utilizing one thread (backgroundworker) at any given time.
/// </summary>
public class AsyncLoggerQueue
{
//create singleton instance of logger queue
public static AsyncLoggerQueue Current = new AsyncLoggerQueue();
private static readonly object logEntryQueueLock = new object();
private Queue<LogEntry> _LogEntryQueue = new Queue<LogEntry>();
private BackgroundWorker _Logger = new BackgroundWorker();
private AsyncLoggerQueue()
{
//configure background worker
_Logger.WorkerSupportsCancellation = false;
_Logger.DoWork += new DoWorkEventHandler(_Logger_DoWork);
}
public void Enqueue(LogEntry le)
{
//lock during write
lock (logEntryQueueLock)
{
_LogEntryQueue.Enqueue(le);
//while locked check to see if the BW is running, if not start it
if (!_Logger.IsBusy)
_Logger.RunWorkerAsync();
}
}
private void _Logger_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
LogEntry le = null;
bool skipEmptyCheck = false;
lock (logEntryQueueLock)
{
if (_LogEntryQueue.Count <= 0) //if queue is empty than BW is done
return;
else if (_LogEntryQueue.Count > 1) //if greater than 1 we can skip checking to see if anything has been enqueued during the logging operation
skipEmptyCheck = true;
//dequeue the LogEntry that will be written to the log
le = _LogEntryQueue.Dequeue();
}
//pass LogEntry to Enterprise Library
Logger.Write(le);
if (skipEmptyCheck) //if LogEntryQueue.Count was > 1 before we wrote the last LogEntry we know to continue without double checking
{
lock (logEntryQueueLock)
{
if (_LogEntryQueue.Count <= 0) //if queue is still empty than BW is done
return;
}
}
}
}
}
I suggest to start with measuring actual performance impact of logging on the overall system (i.e. by running profiler) and optionally switching to something faster like log4net (I've personally migrated to it from EntLib logging a long time ago).
If this does not work, you can try using this simple method from .NET Framework:
ThreadPool.QueueUserWorkItem
Queues a method for execution. The method executes when a thread pool thread becomes available.
MSDN Details
If this does not work either then you can resort to something like John Skeet has offered and actually code the async logging framework yourself.
In response to Sam Safrons post, I wanted to call flush and make sure everything was really finished writting. In my case, I am writing to a database in the queue thread and all my log events were getting queued up but sometimes the application stopped before everything was finished writing which is not acceptable in my situation. I changed several chunks of your code but the main thing I wanted to share was the flush:
public static void FlushLogs()
{
bool queueHasValues = true;
while (queueHasValues)
{
//wait for the current iteration to complete
m_waitingThreadEvent.WaitOne();
lock (m_loggerQueueSync)
{
queueHasValues = m_loggerQueue.Count > 0;
}
}
//force MEL to flush all its listeners
foreach (MEL.LogSource logSource in MEL.Logger.Writer.TraceSources.Values)
{
foreach (TraceListener listener in logSource.Listeners)
{
listener.Flush();
}
}
}
I hope that saves someone some frustration. It is especially apparent in parallel processes logging lots of data.
Thanks for sharing your solution, it set me into a good direction!
--Johnny S
I wanted to say that my previous post was kind of useless. You can simply set AutoFlush to true and you will not have to loop through all the listeners. However, I still had crazy problem with parallel threads trying to flush the logger. I had to create another boolean that was set to true during the copying of the queue and executing the LogEntry writes and then in the flush routine I had to check that boolean to make sure something was not already in the queue and the nothing was getting processed before returning.
Now multiple threads in parallel can hit this thing and when I call flush I know it is really flushed.
public static void FlushLogs()
{
int queueCount;
bool isProcessingLogs;
while (true)
{
//wait for the current iteration to complete
m_waitingThreadEvent.WaitOne();
//check to see if we are currently processing logs
lock (m_isProcessingLogsSync)
{
isProcessingLogs = m_isProcessingLogs;
}
//check to see if more events were added while the logger was processing the last batch
lock (m_loggerQueueSync)
{
queueCount = m_loggerQueue.Count;
}
if (queueCount == 0 && !isProcessingLogs)
break;
//since something is in the queue, reset the signal so we will not keep looping
Thread.Sleep(400);
}
}
Just an update:
Using enteprise library 5.0 with .NET 4.0 it can easily be done by:
static public void LogMessageAsync(LogEntry logEntry)
{
Task.Factory.StartNew(() => LogMessage(logEntry));
}
See:
http://randypaulo.wordpress.com/2011/07/28/c-enterprise-library-asynchronous-logging/
An extra level of indirection may help here.
Your first async method call can put messages onto a synchonized Queue and set an event -- so the locks are happening in the thread-pool, not on your worker threads -- and then have yet another thread pulling messages off the queue when the event is raised.
If you log something on a separate thread, the message may not be written if the application crashes, which makes it rather useless.
The reason goes why you should always flush after every written entry.
If what you have in mind is a SHARED queue, then I think you are going to have to synchronize the writes to it, the pushes and the pops.
But, I still think it's worth aiming at the shared queue design. In comparison to the IO of logging and probably in comparison to the other work your app is doing, the brief amount of blocking for the pushes and the pops will probably not be significant.

Categories

Resources