文件被其他线程占用
本文关键字:线程 其他 文件 | 更新日期: 2023-09-27 18:09:50
我编写了这个静态日志类来记录多个线程期间的所有状态。有时我得到一个异常,说日志文件(程序正在写)被占用了。似乎其他线程在同一时间写文件。我把所有这些工作调用到UI线程,以避免这种异常,但它仍然发生。任何建议吗?谢谢。
顺便说一句,我知道我可以使用锁(mLog)来避免这个问题,但我仍然想知道为什么会发生这种情况,UI线程不应该运行2日志。同时更新elog日志管理系统,对吗?
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);
}
}
对于实现日志记录来说,这绝对不是一个优雅的方法,因为您的类中有多个线程。如果你想要一个更好的设计,你的日志内容必须从表单类中移出,因为日志记录是独立的,线程不应该访问"表单"来"日志"使其有意义。
有两种选择。
-
选择经过测试和验证的日志框架,如log4net或NLog
-
使这个日志类独立,并创建一个logger类的实例(主要是一个单例,尽管我反对单例类),并在多个线程之间共享它。文件管理、日志功能等必须分开管理。所有的操作都必须使用线程同步机制(如互斥锁)来保护。有几种方法可以实现日志框架。这完全取决于你真正需要多少!
除非它不是一个大问题或用于学习目的,否则我建议您使用现有的日志框架,特别是在与生产质量代码一起使用时。
这不是UI线程的问题。问题(主要)在SaveFile方法中。如果两个不同的线程试图访问这个方法,一个线程可能会发现文件仍在被另一个线程使用。一个简单的锁就可以解决这个问题。
假设线程A调用mLogForm.Write
它进入方法并不间断地到达SaveFile方法,它打开文件流,但此时被中断,操作系统决定运行线程B线程B运行并到达相同的SaveFile,发现文件被前一个线程锁定
这里有一个理论:您的日志表单是通过静态变量访问的。这个变量在Log类的第一次访问时初始化,这个第一次访问可以在非ui线程中进行。因此,您的表单可以在非ui线程上创建,这可能会导致您遇到的问题。
我和我的一个朋友一起解决了这个问题。这实际上是因为mLogForm之前从未被展示过。调用invokerrequired。如果没有显示,就永远不会有mLogForm的句柄。没有句柄,您将无法调用mLogForm。以正确的方式调用。这意味着即使其他线程调用Log,它也会返回false。写然后我有很多线程运行UpdateLog方法,导致这个问题。为了确保可以对未显示的表单使用invoke,请在创建此表单时使用CreateHandle()。谢谢。