我编写了这个静态日志类来记录许多线程中的所有状态。有时我得到一个例外,说日志文件(程序正在编写)被占用。似乎其他线程正在同时写入文件。我将所有这些工作调用到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);
}
}
答案 0 :(得分:1)
由于您的类中有多个线程,因此这绝不是实现日志记录的优雅方法。如果你想要一个更好的设计,你的日志文件必须从表单类中移出,因为日志记录是独立的,线程不应该访问“表单”到“日志”使它有意义。
有两种选择。
答案 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()。 感谢。