在c#中,主类创建了一个Logger对象,许多线程都可以访问它。记录器对象看起来像(简化)
public sealed class Logger
{
private ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
public Logger()
{
// create other objects here AND a thread that extracts
// from the queue and writes to a file
// because queue is thread safe this is perfectly ok
}
public void Log(string whatToLog)
{
// Now, is this safe? This method will be called by several threads
// perhaps at the same time
string s = whatToLog + " " + DateTime.Now.ToString();
queue.Enqueue(s);
// The thread created in the constructor will extract and log
}
}
从设计的角度来看,这样可以吗?我的两个问题是:
是“string s = whatToLog +”“+ DateTime.Now.ToString();”好的,如果多个线程同时访问此方法?我想是的,因为任何线程都有自己的s副本,对吗?
如果几个线程只使用Log()方法访问Logger对象,那么一切都安全吗?
由于
答案 0 :(得分:1)
该类非常安全。
一些建议的改进。 该类不会阻止实例化多个实例,如果您希望所有线程都记录到同一个对象,这一点很重要。也许可以应用单身模式。使用静态构造函数的伪单例的快速示例。请注意,默认构造函数是私有的,阻止任何其他类创建记录器。
与性能相关的更改是避免在记录时连接字符串。创建新字符串并不是一个便宜的操作。此外,一旦DateTime.Now转换为字符串,评估就会困难得多。例如。按创建日期和时间等对消息进行排序。在下文中,whatToLog与元组中的DateTime.Now配对。
public sealed class Logger
{
public static Logger instance {get; private set;}
static Logger()
{
instance = new Logger();
}
private ConcurrentQueue<Tuple<string, DateTime>> queue = new ConcurrentQueue<Tuple<string, DateTime>>();
private Logger() {}
public void Log(string whatToLog)
{
queue.Enqueue(new Tuple(whatToLog, DateTime.Now));
}
}
答案 1 :(得分:0)
ConcurrentQueue
将确保队列部分是线程安全的。
您构造的字符串s
不会使其或多或少地具有线程安全性
改进:
internal
并将类放在同一个命名空间带改进的代码
public static class Logger
{
private static ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
public static void Log(string LogMessage)
{
// thread safe logging
queue.Enqueue($"{LogMessage} {DateTime.Now}");
}
//dequeue only within namespace
internal static string Dequeue() {
string dequeuedItem;
queue.TryDequeue(out dequeuedItem);
return dequeuedItem;
}
}
public class LoggerReader
{
public LoggerReader()
{
// create other objects here AND a thread that extracts
// from the queue and writes to a file
// because queue is thread safe this is perfectly ok
string logItem = Logger.Dequeue();
}
}
答案 2 :(得分:-2)
我只是在Log方法中使用一个锁(用Queue替换ConcurrentQueue),不再担心每条指令,特别是如果原始记录器比这里的例子更复杂!
public void Log(string whatToLog)
{
lock(queue) {
string s = whatToLog + " " + DateTime.Now.ToString();
queue.Enqueue(s);
}
}