Dav*_*vid 5 c# mono multithreading const readonly
我一直在努力解决最近出现的一个问题,我写的是一个简单的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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
直到最近,当我注意到我的日志文件没有显示我预期的数据时,这个类工作正常.奇怪的是,我没有改变班级的任何功能.比较老,正与我的新一个版本,唯一的区别是,我的一些领域作了private及readonly.除此之外,string path改为const.令我完全困惑的是,改回来readonly修复我遇到的问题.
所以我的问题是:这究竟是怎么回事?据我所知,在这种情况下,readonly和const之间在功能上应该没有任何区别.
调试时,行为的变化很大,特别是在Run()方法中.这里应该发生的是,一旦Log(queue.Dequeue());被调用,线程将离开lock语句并while (isRunning)再次遍历循环.这看起来很明显,对吗?但是,当我再次更改string pathto const和debug时,Log(queue.Dequeue());会传递一次,并且可以在日志文件中找到单个语句,之后它再也不会执行任何其他操作.它没有while (isRunning)再次过去,它似乎没有离开lock (obj)块.成功调用Log(queue.Dequeue());一次后,记录器线程似乎只是关闭或暂停.
实际上在Log方法中抛出异常没有任何区别,因为日志记录本身工作正常,所以不会抛出异常.
我应该提一下,我在使用Mono的Unity3D 5中使用此代码.但是,通过如此小的编辑,这种行为的剧烈变化对我来说似乎是不可能的.任何人都可以解释为什么会这样吗?
谢谢!
区别如下:
常量是在文件的元数据中创建的,因此当您运行类时,该值已经存在。
ReadOnly 在编译时初始化,在您的情况下,这就是技巧,即使您首先声明路径然后互斥体,编译器也会首先初始化互斥体对象,原因如下:
您要初始化的第一个静态对象是 Logger:
public static readonly TimingLogger Logger = new TimingLogger();
Run Code Online (Sandbox Code Playgroud)
因为您调用了构造函数,所以非静态成员被初始化,从而使互斥体成为下一个要初始化的成员。此时您还没有初始化路径,因此您正在使用参数 false 和 null 创建互斥对象。
如果您希望出现与使用 readonly 的 const 相同的错误,您可以使用静态构造函数强制静态参数初始化的顺序,例如:
static TimingLogger()
{
path = "C:\\Logs\\TimingLog.txt";
Logger = new TimingLogger();
}
Run Code Online (Sandbox Code Playgroud)
或者只是将路径放在记录器之前。
如果您不想使用 const 时出现错误,只需使用 null 参数更改互斥体初始化即可:
private readonly Mutex mutex = new Mutex(false, null);
Run Code Online (Sandbox Code Playgroud)