如何防止在类准备好之前调用类的方法?

时间:2010-06-20 23:24:04

标签: c# class methods locking

还在探索C#...请跟我一起光顾。

这是我的自定义“logger”类,它可以帮助我在项目中创建日志文件。

namespace MyProject
{
class Logger
{
    private FileInfo fFile;
    private DirectoryInfo dDir;

    /// <summary>Add a new entry to the log file.</summary>
    /// <param name="sData">The line to add.</param>
    public void Add(string sData)
    {
        DateTime CurrTime = DateTime.Now;

        if (fFile.Length > 1048576)
        {
            fFile.MoveTo(Path.Combine(dDir.FullName, CurrTime.ToShortDateString() + fFile.Name));
            fFile = new FileInfo(Path.Combine(dDir.FullName,fFile.Name));
            using (StreamWriter sw = fFile.CreateText())
            {
                sw.WriteLine("{0:u}|{1}", CurrTime, sData);
            }
        }
        else
        {
            using (StreamWriter sw = fFile.AppendText())
            {
                sw.WriteLine("{0:u}|{1}", CurrTime, sData);
            }
        }
    }

    /// <summary>Logger instance</summary>
    /// <param name="sFile">Full name of the file to use as logs. Ex : "MyLogs.txt"</param>
    public Logger(string sFile)
    {
        dDir = new DirectoryInfo(Path.Combine(MyProject.AppPath, "logs"));
        if (!dDir.Exists)
        {
            dDir.Create();
        }

        fFile = new FileInfo(Path.Combine(dDir.FullName,sFile));

        if (!fFile.Exists)
        {
            using (StreamWriter sw = fFile.CreateText())
            {
                sw.WriteLine("{0:u}|Logger Started", DateTime.Now);
            }
        }
        else
        {
            Add("Logger Started");
        }           
    }
}
}

我对此代码的问题显然有时候,Logger.Add在该记录器的新实例有时间创建文件之前被调用。所以我的程序崩溃说“找不到文件”,但是,文件最终会在最后创建,如果我使用相同的日志文件名重新启动我的程序,一切正常(因为文件现在存在...)

除了确保在创建文件之前未调用logger.add,有没有办法“锁定”该类?

我已经尝试了锁定方法,但它没有用...锁定(这个)没有做任何事情,我不能在方法本身上使用它。

9 个答案:

答案 0 :(得分:2)

问题在于IO操作已被缓存。这在理论上应该不是问题,但实际上它是。

您可以在构造函数中调用sw.Flush()。这将强制文件从缓存到磁盘,从而创建文件。

if (!fFile.Exists) 
{ 
    using (StreamWriter sw = fFile.CreateText()) 
    { 
        sw.WriteLine("{0:u}|Logger Started", DateTime.Now); 
        sw.Flush();
    } 
}

答案 1 :(得分:2)

实际上引起的异常不是因为文件不存在,而是因为FileInfo实例是陈旧的!您在文件不存在时创建了FileInfo,并且此时它会获取文件状态的快照。当我测试它时,在Add方法中调用fFile.Length时会抛出异常。如果我添加了对fFile.Refresh()的调用,我发现它有效:

...
DateTime currTime = DateTime.Now;
fFile.Refresh();
if (fFile.Length > 1048576)
...

见这里:

http://msdn.microsoft.com/en-us/library/system.io.filesysteminfo.refresh.aspx

“在尝试获取属性信息之前必须进行刷新,否则信息将过时。”

答案 2 :(得分:1)

我个人不会这样做,锁定课程。我会把代码输出来创建日志文件并使其成为自己的方法。然后当调用add方法时,添加一些逻辑来检查日志文件是否存在(在我看来,这个逻辑也应该是它自己的方法)。如果文件存在,继续使用日志记录,如果没有,则创建日志文件(使用上面提取的方法),然后一旦该方法成功返回,继续编写日志。

答案 3 :(得分:1)

在方法中,检查类是否“准备就绪”(无论可能是什么),如果它不是等待,则返回错误或您需要做的任何事情。

答案 4 :(得分:1)

您可以尝试使用FileSystemWatcher类并处理事件以使类准备就绪。这样,事件可以作为委托添加,并且只有在创建了相关文件时才会调用。 Thread.Sleep可能是处理器密集型的,并且在此过程期望完成时会导致其他进程锁定。

答案 5 :(得分:1)

在构造函数中,您要设置字段文件和目录,而应该有一个作为流写器的字段,并在Add方法中使用THAT。

答案 6 :(得分:1)

您希望在创建文件之前阻止add方法运行 - 您可以使用EventWaitHandle或其他一些线程控制机制(可能使用monitor类)来执行此操作。

并且还使用Lock一次限制文件的使用到一个线程,你不希望两个线程同时运行Add - 并且都试图一次移动文件

EventWaitHandle _handle = new EventWaitHandle (false, EventResetMode.ManualReset);
Object _fileLock = new Object();


 public Logger(string sFile)
{

// Do as you do here

_handle.Set();
}


public void Add(string sData)
{
    _handle.WaitOne ();    // Blocks the thread untill Set() is called on _handle


    Lock(_fileLock){    // Only one thread may enter at a time
       // Do as you do already
    }

}

第二个想法,在你的ctor代码周围使用Lock(_fileLock){应该和WaitHandle做同样的工作。

答案 7 :(得分:1)

当构造为与编译器工作相关的fas时,该类应该已准备就绪。除此之外,任何逻辑都必须编程。

    fFile.MoveTo(Path.Combine(dDir.FullName, CurrTime.ToShortDateString() + fFile.Name));
    fFile = new FileInfo(Path.Combine(dDir.FullName,fFile.Name));

这看起来像竞争条件,需要序列化。即使在使用锁(perl / log4perl)之后我们也有一个关闭的问题,并最终为每个进程更喜欢单独的日志文件。 除非有多个Logger对象存活(因为我们处理文件时甚至跨进程),否则它应该不是问题。

奇迹,C#中没有log4等价物。

答案 8 :(得分:0)

编辑 - 当我没有C#编译器时,我在Linux机器上写了这个。在Windows机器上测试了代码后,我发现这里观察到的问题实际上比我的猜测简单得多。请参阅我的其他答案。

你在这里使用多个线程吗?如果你是,你将需要采取不同的方法。例如,如果两个线程同时尝试调用Add并且一个或两个尝试移动文件,则会遇到竞争条件。

当程序运行时,任何其他进程都可以移动或锁定您的日志文件吗?如果有,您的程序是否需要崩溃?如果是这种情况,您将需要进行大量更改,因为所有文件I / O操作都可能失败。

如果这两种情况都不适用,那么看起来您的问题是该文件存储在某个文件系统上,该文件系统在创建文件和您能够观察之间会出现一些延迟那个文件。我不认为这是NTFS或FAT32的情况。您是否将日志文件存储在网络共享中?在不知道更多的情况下,我不能想到比使用rockinthesixstring的测试建议测试文件是否存在以及是否使用短暂的Sleep()调用直到它(或直到你等了足够长的时间才清楚它出了问题 - 也许有人从你身下删除了文件。)

请注意,如果问题 与文件系统相关,则类中没有任何锁定可以提供帮助,也不会调用Flush,因为“using”语句确保StreamWriter已经存在处理,除了与Flush具有相同的效果之外,它将关闭文件,因此Flush是多余的。

所有这一切,为什么你要关闭文件并一直打开它?如果您的程序崩溃,是否确保不会丢失任何内容?如果是这样,你可能最好遵循jmoreno的建议来保持TextWriter打开,但是在你写完每一行之后调用Flush。