最近我编写的一个简单的 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;
}
}
}
直到最近,当我注意到我的日志文件没有显示我期望的数据时,这个类才正常工作。奇怪的是,我没有更改类的任何功能。将一个旧的工作版本与我的新版本进行比较,唯一的区别是我的一些字段被设置为private
和readonly
。除此之外,string path
已更改为 const
。令我完全困惑的是,将其改回 readonly
解决了我遇到的问题。
所以我的问题是:这怎么可能?据我所知,在这种情况下,readonly 和 const 之间在功能上应该没有任何区别。
调试时,行为的变化很大,尤其是在 Run()
方法中。这里应该发生的是一旦Log(queue.Dequeue());
被调用,线程将离开lock
语句并迭代再次通过 while (isRunning)
循环。这看起来很明显,对吧?但是,当我将 string path
更改为 const
并再次调试时, Log(queue.Dequeue());
被传递一次并且可以在日志文件中找到单个语句,之后它就不再做任何其他事情了。它没有再次经过 while (isRunning)
并且似乎没有离开 lock (obj)
block 。成功调用 Log(queue.Dequeue());
一次后,记录器线程似乎只是关闭或暂停。
实际上在 Log
方法中抛出异常没有区别,没有抛出异常,因为日志记录本身工作正常。
我应该提一下,我将此代码与使用 Mono 的 Unity3D 5 一起使用。但是,对我来说,如此小的编辑带来的这种行为上的巨大变化似乎是不可能的。谁能解释为什么会这样?
谢谢!
最佳答案
这是区别:
常量是在文件的元数据中创建的,因此当您运行类时,该值已经存在。
ReadOnly 在编译时初始化,在您的情况下,这就是诀窍,即使您先声明路径然后再声明互斥体,编译器会首先初始化互斥体对象,原因如下:
要初始化的第一个静态对象是记录器:
public static readonly TimingLogger Logger = new TimingLogger();
因为你调用了构造函数,所以非静态成员被初始化,使 mutex 成为下一个要初始化的成员。此时你还没有初始化路径,所以 您正在使用参数 false 和 null 创建互斥锁对象。
如果你想遇到与 const 使用 readonly 时相同的错误,你可以使用静态构造函数强制静态参数初始化的顺序,例如:
static TimingLogger()
{
path = "C:\\Logs\\TimingLog.txt";
Logger = new TimingLogger();
}
或者简单地将路径放在 Logger 之前。
如果你不想使用 const 出现错误,只需使用 null 参数更改互斥锁初始化:
private readonly Mutex mutex = new Mutex(false, null);
关于c# - 将位置字符串更改为 const 会破坏我的记录器类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29792295/