I have an application, that use Spring.AOP library to apply proxy-object to log what methods of program do (I use xml-configuration). Before I used log4net to log messages with Spring.AOP(simplified class):
public class CommandLoggingAdvice : IMethodInterceptor
{
// here I get an instance of log4net
private ILog _Logger = null;
protected ILog Logger
{
_Logger = LogManager.GetLogger("Logger1");
}
public object Invoke(IMethodInvocation invocation)
{
Logger1.Info("Now we enter to method");
// here i call the method
object returnValue = invocation.Proceed();
Logger1.Info("Now we exit from method");
return returnValue;
}
}
But there were a problem: I needed to use a queue of messages, which should work in independent thread to distribute program load on several thread
Here is a new Spring.AOP class:
public class CommandLoggingAdvice : IMethodInterceptor
{
private static ProducerConsumerClass LoggingQueue = ProducerConsumerClass.Instance;
public object Invoke(IMethodInvocation invocation)
{
LoggingQueue.AddTask("Now we enter to method");
// here I call the method
object returnValue = invocation.Proceed();
LoggingQueue.AddTask("Now we exit from method");
return returnValue;
}
}
/// <summary>
/// ProducerConsumerClass implements:
/// - SingleTon-object, Producer/Consumer queue (queue is a FIFO BlockingCollection) - I need this class to process all messages, which come from CommonLoggingAdvice class. The reason is that I need to do it in independent thread (.IsBackground = false)
/// - This version of Singleton class is threadsafe
/// </summary>
public sealed class ProducerConsumerClass : IDisposable
{
// here Iget an instance of log4net
private ILog _Logger = null;
protected ILog Logger
{
_Logger = LogManager.GetLogger("Logger1");
}
private BlockingCollection<string> tasks = new BlockingCollection<string>();
private static volatile ProducerConsumerClass _instance;
private static object locker = new object();
Thread worker;
private ProducerConsumerClass()
{
worker = new Thread(Work);
worker.Name = "Queue thread";
worker.IsBackground = false;
worker.Start();
}
public static ProducerConsumerClass Instance
{
get
{
if (_instance == null)
{
lock (locker)
{
if (_instance == null)
{
_instance = new ProducerConsumerClass();
}
}
}
return _instance;
}
}
public void AddTask(string task)
{
tasks.Add(task);
}
// now this is unused method
// I need to call this method somehow at the end of program, but cross-cutting concern doesn't allow to do it straightahead
public void Dispose()
{
tasks.CompleteAdding();
worker.Join();
tasks.Dispose();
}
void Work()
{
while (true)
{
string task = null;
if (!tasks.IsCompleted)
{
Thread.Sleep(1000);
task = tasks.Take();
Logger1.Info(worker.Name + " " + task );
}
else
{
return;
}
}
}
}
So this class is always running (and so the "worker" thread);
if "tasks" is empty, - tasks.Take()
forces "worker" thread to pause until something will be added using tasks.Add()
.
But when all functions of program are ended and i need to exit from program - "tasks" is empty and "worker" is paused - so I can not exit from infinite cycle => program never ends.
As long as Spring.AOP classes are cross-cutting and they apply automatically, I don't know how to tell "worker" thread ( method Work()
) that it should be completed ( CompleteAdding()
method , or Dispose()
).
Could you help me with this problem or tell any other ways to do what I need:
- cross-cutting concern with Spring.AOP for logging
- threadsafe implemenation of Singleton-class with queue(or Producer/consumer pattern) in independent thread, which live as long as lives application and a little more: until the queue is empty.