创建简单的多线程安全日志记录类的最佳方法是什么?这样的东西就足够了吗?在最初创建日志时,我如何清除它?
public class Logging
{
public Logging()
{
}
public void WriteToLog(string message)
{
object locker = new object();
lock(locker)
{
StreamWriter SW;
SW=File.AppendText("Data\\Log.txt");
SW.WriteLine(message);
SW.Close();
}
}
}
public partial class MainWindow : Window
{
public static MainWindow Instance { get; private set; }
public Logging Log { get; set; }
public MainWindow()
{
Instance = this;
Log = new Logging();
}
}发布于 2010-06-02 11:58:50
不,每次调用该方法时都会创建一个新的锁对象。如果您希望确保一次只有一个线程可以执行该函数中的代码,那么可以将locker移出该函数,将其移到实例或静态成员中。如果每次写入条目时都实例化这个类,那么locker可能应该是静态的。
public class Logging
{
public Logging()
{
}
private static readonly object locker = new object();
public void WriteToLog(string message)
{
lock(locker)
{
StreamWriter SW;
SW=File.AppendText("Data\\Log.txt");
SW.WriteLine(message);
SW.Close();
}
}
}发布于 2013-06-23 18:43:45
下面是一个使用BlockingCollection使用生产者/消费者模式(使用.Net 4)实现的日志示例。界面为:
namespace Log
{
public interface ILogger
{
void WriteLine(string msg);
void WriteError(string errorMsg);
void WriteError(string errorObject, string errorAction, string errorMsg);
void WriteWarning(string errorObject, string errorAction, string errorMsg);
}
}完整的类代码在这里:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Log
{
// Reentrant Logger written with Producer/Consumer pattern.
// It creates a thread that receives write commands through a Queue (a BlockingCollection).
// The user of this log has just to call Logger.WriteLine() and the log is transparently written asynchronously.
public class Logger : ILogger
{
BlockingCollection<Param> bc = new BlockingCollection<Param>();
// Constructor create the thread that wait for work on .GetConsumingEnumerable()
public Logger()
{
Task.Factory.StartNew(() =>
{
foreach (Param p in bc.GetConsumingEnumerable())
{
switch (p.Ltype)
{
case Log.Param.LogType.Info:
const string LINE_MSG = "[{0}] {1}";
Console.WriteLine(String.Format(LINE_MSG, LogTimeStamp(), p.Msg));
break;
case Log.Param.LogType.Warning:
const string WARNING_MSG = "[{3}] * Warning {0} (Action {1} on {2})";
Console.WriteLine(String.Format(WARNING_MSG, p.Msg, p.Action, p.Obj, LogTimeStamp()));
break;
case Log.Param.LogType.Error:
const string ERROR_MSG = "[{3}] *** Error {0} (Action {1} on {2})";
Console.WriteLine(String.Format(ERROR_MSG, p.Msg, p.Action, p.Obj, LogTimeStamp()));
break;
case Log.Param.LogType.SimpleError:
const string ERROR_MSG_SIMPLE = "[{0}] *** Error {1}";
Console.WriteLine(String.Format(ERROR_MSG_SIMPLE, LogTimeStamp(), p.Msg));
break;
default:
Console.WriteLine(String.Format(LINE_MSG, LogTimeStamp(), p.Msg));
break;
}
}
});
}
~Logger()
{
// Free the writing thread
bc.CompleteAdding();
}
// Just call this method to log something (it will return quickly because it just queue the work with bc.Add(p))
public void WriteLine(string msg)
{
Param p = new Param(Log.Param.LogType.Info, msg);
bc.Add(p);
}
public void WriteError(string errorMsg)
{
Param p = new Param(Log.Param.LogType.SimpleError, errorMsg);
bc.Add(p);
}
public void WriteError(string errorObject, string errorAction, string errorMsg)
{
Param p = new Param(Log.Param.LogType.Error, errorMsg, errorAction, errorObject);
bc.Add(p);
}
public void WriteWarning(string errorObject, string errorAction, string errorMsg)
{
Param p = new Param(Log.Param.LogType.Warning, errorMsg, errorAction, errorObject);
bc.Add(p);
}
string LogTimeStamp()
{
DateTime now = DateTime.Now;
return now.ToShortTimeString();
}
}
}在此示例中,用于通过BlockingCollection将信息传递给写入线程的内部Param类为:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Log
{
internal class Param
{
internal enum LogType { Info, Warning, Error, SimpleError };
internal LogType Ltype { get; set; } // Type of log
internal string Msg { get; set; } // Message
internal string Action { get; set; } // Action when error or warning occurs (optional)
internal string Obj { get; set; } // Object that was processed whend error or warning occurs (optional)
internal Param()
{
Ltype = LogType.Info;
Msg = "";
}
internal Param(LogType logType, string logMsg)
{
Ltype = logType;
Msg = logMsg;
}
internal Param(LogType logType, string logMsg, string logAction, string logObj)
{
Ltype = logType;
Msg = logMsg;
Action = logAction;
Obj = logObj;
}
}
}发布于 2010-06-03 14:13:48
使用单个监视器(锁)创建线程安全的日志记录实现不太可能产生积极的结果。虽然您可以正确地这样做,并且已经发布了几个答案来说明如何做到这一点,但这将对性能产生巨大的负面影响,因为执行日志记录的每个对象都必须与执行日志记录的其他每个对象同步。让一个或两个以上的线程同时执行此操作,突然之间,您可能会花费更多的时间等待而不是处理。
使用单监视器方法遇到的另一个问题是,您不能保证线程将按照它们最初请求的顺序获得锁。因此,日志条目实际上可能出现顺序混乱。如果您将其用于跟踪日志记录,这可能会令人沮丧。
多线程是很困难的。轻描淡写地处理它总是会导致错误。
解决此问题的一种方法是实现Producer/Consumer pattern,其中记录器的调用者只需写入内存缓冲区并立即返回,而不是等待记录器写入磁盘,从而显著降低性能损失。日志框架将在单独的线程上使用日志数据并将其持久化。
https://stackoverflow.com/questions/2954900
复制相似问题