Refactor a loop to call an async process - c#

I have a process that goes through a loop. During each iteration of the loop, it calls out to an external web service and then adds a object to an EntityFramework repository. The call to the external service is wrapped in a static method. Typically the loop only has one or two iterations but up to 4 is currently possible with the UI. (Each iteration represents an insurance quote).
It seems that this would benefit from being refactored as an asynchronous process. How do I set this up so that each iteration occurs in a seperate thread, and the commit waits until all threads are completed?
public class ProcessRequest
{
private IUnitOfWork = unitOfWork;
public ProcessRequest(IUnitOfWork uow)
{
unitOfWork = uow;
}
public void Execute(MyRequestParams p)
{
foreach (Quote q in p.Quotes)
{
q.Premium = QuoteService.GetQuote(q);
unitOfWork.GetRepository<Quote>().Add(q);
}
unitOfWork.Commit();
}
}
public static class QuoteService
{
public static decimal GetQuote(Quote quote)
{
//I've simplified proprietary code to single line that calls an external service
return ExternalWebService.GetQuote(quote.Deductible);
}
}

You're asking two different things: one is how to execute the loop in parallel, where each iteration occurs (potentially) on a separate thread; this is completely different to executing the entire loop as an asynchronous process which means the thread that initiates it won't wait for it to complete. I assume you meant the first, i.e. that you want to parallelize the iterations in the loop but still block until all of them are done.
Without knowing anything about the context in which this runs, one straightforward way would be to use Parallel Extensions, specifically Parallel Foreach:
public void Execute(MyRequestParams p)
{
Parallel.ForEach(p.Quotes, q => {
q.Premium = QuoteService.GetQuote(q);
unitOfWork.GetRepository<Quote>().Add(q);
});
unitOfWork.Commit();
}
Or maybe something like:
public void Execute(MyRequestParams p)
{
Parallel.ForEach(p.Quotes, q => {
q.Premium = QuoteService.GetQuote(q);
});
unitOfWork.GetRepository<Quote>().AddAll(p.Quotes);
unitOfWork.Commit();
}
This depends heavily on the thread-safety what you're dealing with.

If most of your work is I/O, im not sure i'd go for spinning up a new thread, as you are wasting most of your time idle waiting for your service/DB to reply.
i'd try to go with a async approach:
public async Task Execute(MyRequestParams p)
{
foreach (var quote in p.Quotes)
{
//Of course, you'll need an async endpoint.
var q.Premium = await QuoteService.GetQuoteAsync(q);
}
unitOfWork.GetRepository<Quote>().AddAll(p.Quotes);
await unitOfWork.SaveChangesAsync();
}
With this approach, you save the overhead of spinning up new threads and letting them be idle most of the time.
Hope this makes sense, of course you'd have to have access to an async endpoint of the webservice, and use Entity Framework v6.

Related

How to do async with sync methods properly?

here is my problem:
i have two TCP-Connections. The first one controls only one machine (M-A) and when it starts the process nothing else is possible to do until it finishes its work. The second connections controls five machines (M-B) which can do their work completely independent from each other or any other process.
For the application: I have a database alert which serves as my EventHandler for Insertion on a specific table. The EventHandler calls a method for getting the inserted Data. Based on the Data i get i want to start either one or two or three or all M-Bs or the M-A.
Since each M-B can start the next job after completion i wanted to run them asynchronously and the M-A synchronously.
Its the first time for me writing asynchronous, so Methods i would like to ask for help to direct me into the right way.
public class OracleAlertListener
{
public OracleAlertListener()
{
//Initialize the alerter
}
private async void Alert_OnAlert(object sender, OracleAlerterAlertEventArgs e)
{
Qm.Getjobs();
}
}
public class QueueManager
{
public void GetJobs(int jnum)
{
var sql = #"select * from table ...";
/*
Get the data into "job". "job" = a single row with command
*/
DoWork(job);
}
public async void DoWork(DataRow job)
{
if (job["ACTION"].Equals("E"))
{
Task mbCommand = ProcessMBAsync(job);
await mbCommand;
}
else
{
var result = ProcessMACommand(job);
}
}
private async Task<DataRow> ProcessMBAsync(DataRow job)
{
await Task.Run(() =>
{
// Do some Work
});
}
private Boolean ProcessMACommand(DataRow job)
{
// Do some work
}
}
Everything i read about asynchronous Programming told me not to use void async methods. When i start my DoWork(job) i don't really need the result of it (also not from GetJobs(int jnum)) rather i need it from the methods ProcessMBAsync and ProcessMACommand.
In ProcessMBAsync i want to know which of the five M-B finished and is ready to start a new job, that's why i return a DataRow to get the information of the job done.
In ProcessMACommand i just want to know if the job was successfull.
So my Question is: How would be the correct way to construct this procedure?
EDIT: I mean how to construct this procedure, so that i can avoid an async void Method. Run the jobs for M-B asynchrounsly and afterwards know which one finished (since the M-A is synchron and i will always know when it finishes the job).
The AlertListener is rather obsolet (it isn't necessary, just convenient) and i don't want to build this procedure focusing on this Eventhandler.
Thanks for your advices.
Btw: i read these articels :https://msdn.microsoft.com/en-us/magazine/jj991977.aspx https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/0ba7625050f975f8a7df1df57c80ad08da250541/AsyncGuidance.md and several other threads on stackoverflow.com

Multithread queue of jobs

I have a queue of jobs which can be populated by multiple threads (ConcurrentQueue<MyJob>). I need to implement continuous execution of this jobs asynchronously(not by main thread), but only by one thread at the same time. I've tried something like this:
public class ConcurrentLoop {
private static ConcurrentQueue<MyJob> _concurrentQueue = new ConcurrentQueue<MyJob>();
private static Task _currentTask;
private static object _lock = new object();
public static void QueueJob(Job job)
{
_concurrentQueue.Enqueue(job);
checkLoop();
}
private static void checkLoop()
{
if ( _currentTask == null || _currentTask.IsCompleted )
{
lock (_lock)
{
if ( _currentTask == null || _currentTask.IsCompleted )
{
_currentTask = Task.Run(() =>
{
MyJob current;
while( _concurrentQueue.TryDequeue( out current ) )
//Do something
});
}
}
}
}
}
This code in my opinion have a problem: if task finnishing to execute(TryDequeue returns false but task have not been marked as completed yet) and in this moment i get a new job, it will not be executed. Am i right? If so, how to fix this
Your problem statement looks like a producer-consumer problem, with a caveat that you only want a single consumer.
There is no need to reimplement such functionality manually.
Instead, I suggest to use BlockingCollection -- internally it uses ConcurrentQueue and a separate thread for the consumption.
Note, that this may or may not be suitable for your use case.
Something like:
_blockingCollection = new BlockingCollection<your type>(); // you may want to create bounded or unbounded collection
_consumingThread = new Thread(() =>
{
foreach (var workItem in _blockingCollection.GetConsumingEnumerable()) // blocks when there is no more work to do, continues whenever a new item is added.
{
// do work with workItem
}
});
_consumingThread.Start();
Multiple producers (tasks or threads) can add work items to the _blockingCollection no problem, and no need to worry about synchronizing producers/consumer.
When you are done with producing task, call _blockingCollection.CompleteAdding() (this method is not thread safe, so it is advised to stop all producers beforehand).
Probably, you should also do _consumingThread.Join() somewhere to terminate your consuming thread.
I would use Microsoft's Reactive Framework Team's Reactive Extensions (NuGet "System.Reactive") for this. It's a lovely abstraction.
public class ConcurrentLoop
{
private static Subject<MyJob> _jobs = new Subject<MyJob>();
private static IDisposable _subscription =
_jobs
.Synchronize()
.ObserveOn(Scheduler.Default)
.Subscribe(job =>
{
//Do something
});
public static void QueueJob(MyJob job)
{
_jobs.OnNext(job);
}
}
This nicely synchronizes all incoming jobs into a single stream and pushes the execution on to Scheduler.Default (which is basically the thread-pool), but because it has serialized all input only one can happen at a time. The nice thing about this is that it releases the thread if there is a significant gap between the values. It's a very lean solution.
To clean up you just need call either _jobs.OnCompleted(); or _subscription.Dispose();.

Thread Pooling for a method which has a return type

I have a method named InitializeCRMService() which returns an object of IOrganizationService. Now I am defining a different method named GetConnection(string thread) which calls InitializeCRMService() based on the parameter passed to it. If the string passed to GetConnection is single it will start a single threaded instance of the IntializeCRMService() method, but if the string passed is multiple, I need to use a thread pool where I need to pass the method to QueueUserWorkItem. The method InitializeCRMService has no input parameters. It just returns a service object. Please find below the code block in the GetConnection method:
public void GetConnection(string thread)
{
ParallelOptions ops = new ParallelOptions();
if(thread.Equals("one"))
{
Parallel.For(0, 1, i =>
{
dynamic serviceObject = InitializeCRMService();
});
}
else if (thread.Equals("multi"))
{
// HERE I NEED TO IMPLEMENT MULTITHREADING USING THREAD POOL
// AND NOT PARALLEL FOR LOOP......
// ThreadPool.QueueUserWorkItem(new WaitCallback(InitializeCRMService));
}
}
Please note my method InitializeCRMService() has a return type of Service Object.
Please tell me how do I implement it.
Since you want to execute InitializeCRMService in the ThreadPool when a slot is available, and you are executing this only once, the solution depends on what you want to do with the return value of InitializeCRMService.
If you only want to ignore it, I have two options so far.
Option 1
public void GetConnection(string thread)
{
//I found that ops is not being used
//ParallelOptions ops = new ParallelOptions();
if(thread.Equals("one"))
{
Parallel.For(0, 1, i =>
{
//You don't really need to have a variable
/*dynamic serviceObject =*/ InitializeCRMService();
});
}
else if (thread.Equals("multi"))
{
ThreadPool.QueueUserWorkItem
(
new WaitCallback
(
(_) =>
{
//You don't really need to have a variable
/*dynamic serviceObject =*/ InitializeCRMService();
}
)
);
}
}
On the other hand, if you need to pass it somewhere to store it an reuse it later you can do it like this:
public void GetConnection(string thread)
{
//I found that ops is not being used
//ParallelOptions ops = new ParallelOptions();
if(thread.Equals("one"))
{
Parallel.For(0, 1, i =>
{
//It seems to me a good idea to take the same path here too
//dynamic serviceObject = InitializeCRMService();
Store(InitializeCRMService());
});
}
else if (thread.Equals("multi"))
{
ThreadPool.QueueUserWorkItem
(
new WaitCallback
(
(_) =>
{
Store(InitializeCRMService());
}
)
);
}
}
Where Store would be something like this:
private void Store(dynamic serviceObject)
{
//store serviceObject somewhere you can use it later.
//Depending on your situation you may want to
// set a flag or use a ManualResetEvent to notify
// that serviceObject is ready to be used.
//Any pre proccess can be done here too.
//Take care of thread affinity,
// since this may come from the ThreadPool
// and the consuming thread may be another one,
// you may need some synchronization.
}
Now, if you need to allow clients of your class to access serviceObject, you can take the following approach:
//Note: I marked it as partial because there may be other code not showed here
// in particular I will not write the method GetConnection again. That said...
// you can have it all in a single block in a single file without using partial.
public partial class YourClass
{
private dynamic _serviceObject;
private void Store(dynamic serviceObject)
{
_serviceObject = serviceObject;
}
public dynamic ServiceObject
{
get
{
return _serviceObject;
}
}
}
But this doesn't take care of all the cases. In particular if you want to have thread waiting for serviceObject to be ready:
public partial class YourClass
{
private ManualResetEvent _serviceObjectWaitHandle = new ManualResetEvent(false);
private dynamic _serviceObject;
private void Store(dynamic serviceObject)
{
_serviceObject = serviceObject;
//If you need to do some work as soon as _serviceObject is ready...
// then it can be done here, this may still be the thread pool thread.
//If you need to call something like the UI...
// you will need to use BeginInvoke or a similar solution.
_serviceObjectWaitHandle.Set();
}
public void WaitForServiceObject()
{
//You may also expose other overloads, just for convenience.
//This will wait until Store is executed
//When _serviceObjectWaitHandle.Set() is called
// this will let other threads pass.
_serviceObjectWaitHandle.WaitOne();
}
public dynamic ServiceObject
{
get
{
return _serviceObject;
}
}
}
Still, I haven't covered all the scenarios. For intance... what happens if GetConnection is called multiple times? We need to decide if we want to allow that, and if we do, what do we do with the old serviceObject? (do we need to call something to dismiss it?). This can be problematic, if we allow multiple threads to call GetConnection at once. So by default I will say that we don't, but we don't want to block the other threads either...
The solution? Follows:
//This is another part of the same class
//This one includes GetConnection
public partial class YourClass
{
//1 if GetConnection has been called, 0 otherwise
private int _initializingServiceObject;
public void GetConnection(string thread)
{
if (Interlocked.CompareExchange(ref _initializingServiceObject, 1, 0) == 0)
{
//Go on, it is the first time GetConnection is called
//I found that ops is not being used
//ParallelOptions ops = new ParallelOptions();
if(thread.Equals("one"))
{
Parallel.For(0, 1, i =>
{
//It seems to me a good idea to take the same path here too
//dynamic serviceObject = InitializeCRMService();
Store(InitializeCRMService());
});
}
else if (thread.Equals("multi"))
{
ThreadPool.QueueUserWorkItem
(
new WaitCallback
(
(_) =>
{
Store(InitializeCRMService());
}
)
);
}
}
}
}
Finally, if we are allowing multiple thread to use _serviceObject, and _serviceObject is not thread safe, we can run into trouble. Using monitor or using a read write lock are two alternatives to solve that.
Do you remember this?
public dynamic ServiceObject
{
get
{
return _serviceObject;
}
}
Ok, you want to have the caller access the _serviceObject when it is in a context that will prevent others thread to enter (see System.Threading.Monitor), and make sure it stop using it, and then leave this context I mentioned before.
Now consider that the caller thread could still store a copy of _serviceObject somewhere, and then leave the syncrhonization, and then do something with _serviceObject, and that may happen when another thread is using it.
I'm used to think of every corner case when it comes to threading. But if you have control over the calling threads, you can do it very well with just the property showed above. If you don't... let's talk about it, I warn you, it can be extensive.
Option 2
This is a totally different behaviour, the commend Damien_The_Unbeliever made in your question made me think that you may have intended to return serviceObject. In that case, it is not shared among threads, and it is ok to have multiple serviceObject at a time. And any synchronization needed is left to the caller.
Ok, this may be what you have been looking for:
public void GetConnection(string thread, Action<dynamic> callback)
{
if (ReferenceEquals(callback, null))
{
throw new ArgumentNullException("callback");
}
//I found that ops is not being used
//ParallelOptions ops = new ParallelOptions();
if(thread.Equals("one"))
{
Parallel.For(0, 1, i =>
{
callback(InitializeCRMService());
});
}
else if (thread.Equals("multi"))
{
ThreadPool.QueueUserWorkItem
(
new WaitCallback
(
(_) =>
{
callback(InitializeCRMService());
}
)
);
}
}
How should the callback look? Well, as soon as it is not shared between threads it is ok. Why? Because each thread that calls GetConnection passes it's own callback Action, and will recieve a different serviceObject, so there is no risk that what one thread does to it affect what the other does to its (since it is not the same serviceObject).
Unless you want to have one thread call this and then shared it with other threads, in which case, it is a problem of the caller and it will be resolved in another place in another moment.
One last thing, you could use an enum to represent the options you currently pass in the string thread. In fact, since there are only two options you may consider using a bool, unless they may appear more cases in the future.

How can I get my application to continue, only when all async operations are complete?

In my application, I used to create along string of async operations, which passed in functions like this:
public void LotsOfAsync()
{
DoAsync1( ()=> { DoAsync2( ()=> { doAsync3( ()=> { finalAction();}) } ) } );
}
However, now I have moved many of those async operations into separate classes and objects but I want the same results. Mainly I have moved those async operations into classes which become part of a collection.
I'd like my new code to look like this:
public void lotsOfAsync()
{
DoAsync1();
for each ( MyClass in Async1List)
{
MyClass.DoAsyn2();
}
if (allAsyncOperationsAreComplete)
{
FinalAction();
}
}
What things do I need to do, to get this to work? Thanks.
Using the Answer below about Tasks, something still seems to be lacking. The program never continues even after throwing everything into a BackgroundWorker.
You can use Task<T> (using the Task Parallel Library for Silverlight) - something like this maybe:
List<Task> tasks = new List<Task>();
Task.Factory.StartNew(() => DoAsync1()).ContinueWith(_ =>
{
foreach (MyClass myClass in Async1List)
{
tasks.Add(Task.Factory.StartNew(() => myClass.DoSomething()));
}
Task.WaitAll(tasks.ToArray());
FinalAction();
});
Im not familiar with wp7, but you may use counter as static field and check if it's equal to 0 in final action.
Every MyClass.DoAsyn2() should fire maybe a event, or any other code to signal that it is finished.
Another option is to move all async to task and call Task.WaitAll
http://msdn.microsoft.com/en-us/library/dd270695.aspx
Have you had a look at the CountdownEvent in .Net 4? This is a signalling construct where one thread will block and only proceed once other threads have completed and called set on the count down event. You initialise it with the number of signals you need before the thread calling Wait on the construct will proceed. E.g.:
CountdownEvent countdown = new CountdownEvent(3);
will only let the thread calling Wait to proceed once 3 other threads have called Signal.
So your example would perhaps look something like this:
public void lotsOfAsync()
{
Int32 numberOfAsyncProcesses = Async1List.Length + 1;
CountdownEvent countdown = new CountdownEvent (numberOfAsyncProcesses);
DoAsync1(countdown); // call countdown.signal() in the async method once complete.
for each ( MyClass in Async1List)
{
// call countdown.signal() in the async method once complete.
MyClass.DoAsyn2(countdown);
}
if(countDown.Wait(TimeSpan.FromSeconds(3))
{
FinalAction();
}
}
I've also added a timeout where the calling thread will unblock after 3 seconds if failed to get a response from all processing threads. In this case, the final action will not be performed.
You can reproduce this with Monitor/Pulse if you are not targeting .Net 4.
There is a nice example here too. Hope this helps!
After looking through all the previous answers, I was unable to solve my problem.
Instead what I needed to do, was create custom Events within my classes, which triggered upon a successful completion of the asynchronous tasks.
The website that proved the most useful to me to accomplish this was: http://www.marten-online.com/csharp/simple-custom-event-handling.html
My final code looked something like this:
public void lotsOfAsync()
{
DoAsync1();
for each ( MyClass in Async1List)
{
MyClass.PropertyChange += new MyClass.PropertyChangeHandler(Async2Complete);
MyClass.DoAsyn2();
}
}
public void Async2Complete(object sender, PropertyChangeEventArgs data)
{
if (data.PropertyName == "AsyncComplete")
{
totalAsyncCompleted++;
if (totalAsyncCompleted >= Async1List.Count)
{
FinalAction();
}
}
}
Have you heard of the Deferred pattern often used in Javascript?
It is simple to work with and very dynamic and you should be able to implement it on Windows phone aswell.
Have a look at this guide
http://twistedmatrix.com/documents/current/core/howto/defer.html
Regards
Tobias

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