将位置字符串更改为const会破坏我的记录器类



我最近写了一个简单的logtofile类,遇到了一个问题。

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace Assets.Code
{
class TimingLogger
{
public static readonly TimingLogger Logger = new TimingLogger();
private static readonly string path = "C:\Logs\TimingLog.txt";
private readonly Mutex mutex = new Mutex(false, path);
private StreamWriter writer;
private readonly Queue<string> queue = new Queue<string>();
private bool isRunning;
private readonly object obj = new object();
private TimingLogger()
{
}
public void CheckPath()
{
if (!File.Exists(path))
{
File.Create(path);
}
}
public void Run()
{
isRunning = true;
while (isRunning)
{
lock (obj)
{
while (queue.Count <= 0)
{
Monitor.Wait(obj);
}
Log(queue.Dequeue());
}
}
}
public void Log(string line)
{
try
{
mutex.WaitOne();
writer = File.AppendText(path);
writer.WriteLine(line);
writer.Close();
}
catch (Exception)
{
//throw;
}
finally
{
mutex.ReleaseMutex();
}
}
public void Enqueue(string line)
{
lock (obj)
{
queue.Enqueue(line);
Monitor.Pulse(obj);
}
}
public void Stop()
{
isRunning = false;
}
}
}

直到最近,当我注意到我的日志文件没有显示我期望的数据时,这个类才正常工作。奇怪的是,我没有改变这个类的任何功能。将旧的工作版本与我的新版本进行比较,唯一的区别是我的一些字段被设置为privatereadonly。除此之外,将string path改为const。令我完全困惑的是,将其改回readonly解决了我的问题。

所以我的问题是:这到底是怎么可能的?据我所知,在这种情况下,readonly和const在功能上应该没有任何区别。

调试时,行为的变化是显著的,尤其是在Run()方法中。这里应该发生的是,一旦调用了Log(queue.Dequeue());,线程将离开lock语句,并再次迭代while (isRunning)循环。这似乎很明显,对吧?然而,当我将string path更改为const并再次调试时,Log(queue.Dequeue());会被传递一次,并且可以在日志文件中找到一条语句,之后它就再也不做任何其他事情了。它不会再次经过while (isRunning),并且似乎不会离开lock (obj)块。记录器线程似乎只是在成功调用Log(queue.Dequeue());一次后关闭或暂停。

实际上,在Log方法中抛出异常并没有什么区别,因为日志记录本身运行良好,所以不会抛出异常。

我应该提到的是,我在Unity3D5中使用了这段代码,它使用了Mono。但是,这样一个小的编辑在行为上的巨大变化对我来说似乎是不可能的。有人能解释为什么会发生这种情况吗?

谢谢!

区别如下:

const是在文件的元数据中创建的,因此当您运行类时,值已经存在。

ReadOnly是在编译时初始化的,在您的情况下,这就是诀窍,即使您先声明了路径,然后声明了互斥,编译器也先初始化了互斥对象,原因如下:

要初始化的第一个静态对象是Logger:

public static readonly TimingLogger Logger = new TimingLogger();

因为调用了构造函数,所以非静态成员被初始化,从而使互斥体成为下一个要初始化的成员。此时您还没有初始化路径,所以您正在创建参数为false和null的互斥对象。

如果你想使用只读的const出现同样的错误,你可以使用静态构造函数强制静态参数初始化的顺序,比如:

static TimingLogger()
{
path = "C:\Logs\TimingLog.txt";
Logger = new TimingLogger();
}

或者简单地将路径放在Logger之前。

如果您不想使用const出现错误,只需使用null参数更改互斥初始化即可:

private readonly Mutex mutex = new Mutex(false, null);

最新更新