views:

200

answers:

3

I am using a System.Threading.ThreadPool to manage a queue of jobs from a service. I have already implemented logging like this...

abstract class Global
{
    public static LogFile LogFile = null;
}

public class LogFile : IDisposable
{
    private StreamWriter sw;
    public LogFile(string path){}
    public void WriteEntry(string logText)
    {
        lock (sw)
        {
            sw.WriteLine(logText);
        }
    }
}

I want to create the log at service startup and use it from my queued worker threads.. something like this...

//On Service Start
Global.LogFile = new LogFile("log.txt");

//Kick of worker thread
ThreadPool.QueueUserWorkItem(objWrkrThread.CallbackMethod, iCount);

//Worker thread logs an entry in CallbackMethod()
Global.LogFile.WriteEntry("Hello World");

Is this safe? Will calling a method on a static instance of a class inadvertently 'synchronise' or 'block' my threads?

Michael

A: 

It's not safe to have multiple threads call WriteEntry at the same time unless it was designed to be safe.

Steven Sudit
It turns out that Scotty's example of how to do singletons under .NET is too complicated. Instead, you can avoid the explicit locking just by writing something like:private static Log _instance = new Log(_path)
Steven Sudit
+3  A: 

Nothing will 'synchronize' or 'block' unless you write code in your method. It doesn't matter whether it's an instance method or static method.

So by default, WriteEntry won't block any calls from your threads but it could very well corrupt file if you don't write the code to handle multiple simultaneous calls.

Read more on this topic here:

http://stackoverflow.com/questions/1090650/are-static-methods-thread-safe

SolutionYogi
A: 

What you are trying to do sounds like the perfect candidate for a Singleton class. I know it gets a bad wrap, but sometimes it's simplicity is worth it.

You can create a log class like this and you should be thread safe.

public sealed class Log
{
    static Log instance=null;
    static readonly object lockObject = new object();
    static string path = "log.txt";

    Log()
    {
    }

    public static Log Instance
    {
      get
      {
            lock (lockObject)
            {
                if (instance==null)
                {
                    instance = new Log();
                }
                return instance;
            }
       }
    }

    public void WriteLine(string message)
    {
       lock(lockObject)
       {
          using(StreamWriter sw = new StreamWriter(File.Open(path, FileMode.Append)))
          {
             sw.WriteLine(message);
          }
       }
    }
}

Then in your code, you just call it like this:

Log executionLog = Log.Instance;
executionLog.WriteLine("this is a log message.");

You could also manage opening the file in similar thread safe methods to get rid of the over head of opening and closing the file every write.

scottm
interesting idea, thanks for taking the time to puch out the code scotty.
Michael Dausmann
Are you sure the above code will compile? lock is a keyword and you can't use it as the name of your object.
SolutionYogi
@SolutionYogi, you are correct. I just typed it out, no testing. I updated my answer.
scottm