C# lock issues when writing to log

心已入冬 提交于 2020-01-17 06:34:14

问题


I have a class that programmatically creates a log using the Nlog framework. I have several processes running and creating their logs at the same time. I added a lock to the constructor of the class, as before the two threads were attempting to create a file at the same time which resulted in some annoying bugs (like only creating one log).

This seems to have solved that problem. However now i have the same issue with writing to the log, and using a lock has not helped. Here is the class.

public class CwiLogger
    {
        private Logger _log;
        private static Object createLogLock = new Object();
        private static Object writeLogLock = new Object();
        public CwiLogger(string logPath, string logName, string className)
        {
            lock (createLogLock)
            {
                var config = new LoggingConfiguration();
                var fileTarget = new FileTarget();
                config.AddTarget("file", fileTarget);
                fileTarget.FileName = Path.Combine(logPath, logName);
                fileTarget.Layout = "${longdate}|${level:uppercase=true}|${logger}|${message}";
                var rule = new LoggingRule("*", LogLevel.Debug, fileTarget);
                config.LoggingRules.Add(rule);

                LogManager.Configuration = config;

                this._log = LogManager.GetLogger(className);
            }
        }

        public void AddToLog(string logText, LogLevel level = null)
        {
            lock (writeLogLock)
            {
                level = level ?? LogLevel.Info;
                this._log.Log(level, logText + "\n");
            }
        }
    }

In my client code i run two threads each running a process that looks like this:

var log = new CwiLogger(@"C:\Users\jma\Documents\ProgrammingJunk\logTest", "Log2.txt", "Log2");
for (int i = 0; i < 100; i++)
{
    log.AddToLog("Log2 " + i);
}

only i use log1 for one and log2 for the other.

in my output. One of the 2 logs always sucessfully counts to 99, while the other gets 4-5 in and then has no other output.


回答1:


Maybe something like this:

public class CwiLogger
{
    private static LoggingConfiguration _logConfig = new LoggingConfiguration();
    private static Object createLogLock = new Object();
    private Logger _log;

    public CwiLogger(string logPath, string logName, string className)
    {
            lock (createLogLock)
            {
                var fileTarget = _logConfig.FindTargetByName(logName);
                if (fileTarget == null)
                {
                    var fileTarget = new FileTarget(logName);
                    fileTarget.FileName = Path.Combine(logPath, logName);
                    fileTarget.Layout = "${longdate}|${level:uppercase=true}|${logger}|${message}";
                    _logConfig.AddTarget(fileTarget);
                    var rule = new LoggingRule(className, LogLevel.Debug, fileTarget) { Final = true };
                    _logConfig.LoggingRules.Add(rule);
                    LogManager.Configuration = _logConfig;
                }
            }
        this._log = LogManager.GetLogger(className);
    }

    public void AddToLog(string logText, LogLevel level = null)
    {
        level = level ?? LogLevel.Info;
        this._log.Log(level, logText + "\n");
    }
}

Or maybe steal some ideas from here: https://github.com/NLog/NLog/issues/1998




回答2:


This is because the lock() syntax compiles to Monitor.Enter and Monitor.Leave - Monitor.Enter will put the current thread to sleep if it cannot lock the object, the thread holding the lock and will then awake all waiting threads when it calls Monitor.Leave.

Your problem is that your second thread has to go through the additional time of waking up before it can attempt to lock the object, by which time the thread that just released the lock has already locked it again.

If you expect your routine to lock only for a very short time (too short to want to sleep waiting threads) use a SpinLock instead. This class gets each thread to keep attempting the lock in a loop until it succeeds rather than sleeping. It means it uses more CPU (and thus more battery power on a laptop), which is why the code you lock must run for very small amounts of time.



来源:https://stackoverflow.com/questions/43125584/c-sharp-lock-issues-when-writing-to-log

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!