1

我编写了这个静态日志类来记录许多线程期间的所有状态。有时我得到一个异常,说日志文件(程序正在写入)被占用。似乎其他线程正在同时写入文件。我将所有这些工作都调用到 UI 线程以避免此异常,但它仍然会发生。有什么建议吗?谢谢。

顺便说一句,我知道我可以使用 lock(mLog) 来避免这个问题,但我仍然想知道为什么会这样,UI 线程不应该同时运行 2 个 Log.UpdateLog 函数,对吗?

public partial class LogForm : Form
{
    private StringBuilder mLog;

    public LogForm()
    {
        InitializeComponent();

        mLog = new StringBuilder();
    }

    public void Write(string msg, bool save)
    {
        mLog.Insert(0, msg + "\r\n\r\n" + "-----------------------------------------------------------------------" + "\r\n\r\n");

        if (save)
        {
            SaveFile();
        }
    }

    private void SaveFile()
    {
        FileStream file;
        file = new FileStream(Application.StartupPath + @"\LOG.txt", FileMode.Create);
        StreamWriter sw = new StreamWriter(file);
        sw.Write(mLog.ToString());
        sw.Close();
        file.Close();
    }


}

public static class Log
{
    private delegate void mUIInvoke(string msg, bool save);

    private static LogForm mLogForm = new LogForm();

    public static void Write(string msg, bool save)
    {
        msg += "\r\nTIME:" + DateTime.Now.ToShortDateString() + "  " + DateTime.Now.ToShortTimeString();

        if (mLogForm.InvokeRequired)
        {
            mUIInvoke invoke = new mUIInvoke(UpdateLog);
            mLogForm.BeginInvoke(invoke, new object[] { msg, save });
        }
        else
        {
            UpdateLog(msg, save);
        }
    }


    private static void UpdateLog(string msg, bool save)
    {
        mLogForm.Write(msg, save);
    }

}
4

4 回答 4

1

这绝对不是实现日志记录的优雅方法,因为您的类中有多个线程。如果您想要更好的设计,则必须将日志记录从表单类中移出,因为日志记录是独立的,线程不应访问“表单”以“记录”使其有意义。

有两种选择。

  1. 选择经过测试和证明的日志框架,如log4netNLog

  2. 使此日志记录类独立并创建记录器类的实例(尽管我反对单例类,但主要是单例)并在多个线程之间共享它。文件管理、日志功能等必须单独管理。所有操作都必须使用互斥锁等线程同步机制进行保护。有几种方法可以实现日志框架。这完全取决于你真正需要多少!

除非没什么大不了的或出于学习目的,否则我建议您使用现有的日志记录框架,尤其是在与生产质量代码一起使用时。

于 2013-04-19T04:42:13.330 回答
0

这是一个理论:您的日志记录表单是通过静态变量访问的。这个变量在第一次访问 Log 类时被初始化,第一次访问可以从非 ui 线程发生。因此,您的表单可以在非 ui 线程上创建,这可能会导致您遇到的问题。

于 2013-04-18T20:18:21.110 回答
0

这不是 UI 线程的问题。问题(主要)出在 SaveFile 方法中。如果两个不同的线程尝试访问此方法,其中一个可能会发现另一个线程仍在使用该文件。一个简单的锁就可以解决这个问题。

所以想象调用mLogForm.Write 它的线程 A 它进入方法并不间断地到达 SaveFile 方法,它打开文件流但此时被中断并且操作系统决定运行线程 B 线程 B 运行并到达相同的 SaveFile 找到被锁定的文件上一个线程挂起

于 2013-04-18T20:10:29.747 回答
0

我和我的一位朋友发现了这个问题。这实际上是因为在调用 mLogForm.InvokeRequired 之前从未显示过 mLogForm。如果未显示,则永远不会有 mLogForm 的句柄。如果没有句柄,您将无法以正确的方式调用 mLogForm.InvokeRequired。这意味着即使其他线程调用 Log.Write 也会返回 false,然后我有很多线程运行 UpdateLog 方法,导致了这个问题。为确保您可以对未显示的表单使用调用,请在创建此表单时使用 CreateHandle()。谢谢。

于 2013-04-19T04:26:30.607 回答