其他线程占用的文件

时间:2013-04-18 19:59:57

标签: c# multithreading

我编写了这个静态日志类来记录许多线程中的所有状态。有时我得到一个例外,说日志文件(程序正在编写)被占用。似乎其他线程正在同时写入文件。我将所有这些工作调用到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 个答案:

答案 0 :(得分:1)

由于您的类中有多个线程,因此这绝不是实现日志记录的优雅方法。如果你想要一个更好的设计,你的日志文件必须从表单类中移出,因为日志记录是独立的,线程不应该访问“表单”到“日志”使它有意义。

有两种选择。

  1. 转到经过测试和验证的记录框架,如log4netNLog

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

  3. 除非这不是一个大问题或用于学习目的,否则我建议您使用现有的日志记录框架,尤其是在使用生产质量代码时。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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