vb.net - 将其放在后台工作人员上会停止此问题
问题描述
我已经尝试解决这个问题好几天了,但没有任何成功。我知道我已经创建了另一个与此问题相关的帖子,但不确定我是否应该继续另一个帖子而不是创建一个新帖子,因为我对 SO 的工作方式仍然很陌生,所以如果我以错误的方式处理这个问题,我深表歉意.
目标
从磁盘读取文本文件,将内容输出到文本框,然后我可以从中提取最后 3 行。这是我能想到的唯一方法。
该文本文件由另一个正在运行的程序不断更新,但即使它正在使用但我仍然可以读取它但无法写入它。
我正在通过每 1 秒滴答一次的计时器来探测此文件,以获取最新信息。
现在的问题......
我注意到一段时间后我的应用程序变得迟缓,当我尝试在屏幕上移动它或调整它的大小并且 CPU 使用率开始攀升至超过 33% 时,这一点很明显
我的思考过程
由于读取文件是一个连续的过程,我想我可以将它移动到 BackgroundWorker 上,据我了解,它会将它放在不同的线程上并减轻主 GUI 的负载。
我在这棵树上叫错了吗?
在开始学习如何使用 BackgroundWorker 的所有教科书之前,我正在接触更高级的用户。
这是我用来读取 txt 文件并将其输出到文本框的代码。我没有包含用于提取最后 3 行的代码,因为我认为这不是导致问题的部分。
我认为这个问题是因为我每秒钟都在用计时器不断地探测源文件,但不是 100% 肯定是诚实的。
Dim strLogFilePath As String
strLogFilePath = "C:\DSD\data.txt"
Dim LogFileStream As FileStream
Dim LogFileReader As StreamReader
'Open file for reading
LogFileStream = New FileStream(strLogFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
LogFileReader = New StreamReader(LogFileStream)
'populate text box with the contents of the txt file
Dim strRowText As String
strRowText = LogFileReader.ReadToEnd()
TextBox1.text = strRowText
'Clean Up
LogFileReader.Close()
LogFileStream.Close()
LogFileReader.Dispose()
LogFileStream.Dispose()
解决方案
首先,您应该使用Using
关键字而不是手动释放对象,因为这样可以保证对象将被释放,即使发生意外异常,例如:
' You can initialize variables in one line
Dim strLogFilePath As String = "C:\DSD\data.txt"
Using LogFileStream As New FileStream(strLogFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
' Everything goes in here
End Using
我的解决方案不需要读者。读数将手动完成。
接下来,您需要读取流的最后 n 行(在您的情况下为 3 行)。当您只对最后几行感兴趣时读取整个文件是低效的。相反,您可以从头开始阅读,直到达到三个(或任意数量)行分隔符(基于此答案):
Function ReadLastLines(
NumberOfLines As Integer, Encoding As System.Text.Encoding, FS As FileStream,
Optional LineSeparator As String = vbCrLf
) As String
Dim NewLineSize As Integer = Encoding.GetByteCount(LineSeparator)
Dim NewLineCount As Integer = 0
Dim EndPosition As Long = Convert.ToInt64(FS.Length / NewLineSize)
Dim NewLineBytes As Byte() = Encoding.GetBytes(LineSeparator)
Dim Buffer As Byte() = Encoding.GetBytes(LineSeparator)
For Position As Long = NewLineSize To EndPosition Step NewLineSize
FS.Seek(-Position, SeekOrigin.End)
FS.Read(Buffer, 0, Buffer.Length)
If Encoding.GetString(Buffer) = LineSeparator Then
NewLineCount += 1
If NewLineCount = NumberOfLines Then
Dim ReturnBuffer(CInt(FS.Length - FS.Position)) As Byte
FS.Read(ReturnBuffer, 0, ReturnBuffer.Length)
Return Encoding.GetString(ReturnBuffer)
End If
End If
Next
' Handle case where number of lines in file is less than NumberOfLines
FS.Seek(0, SeekOrigin.Begin)
Buffer = New Byte(CInt(FS.Length)) {}
FS.Read(Buffer, 0, Buffer.Length)
Return Encoding.GetString(Buffer)
End Function
用法:
Using LogFileStream As New FileStream(strLogFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
' Depending on system, you may need to supply an argument for the LineSeparator param
Dim LastThreeLines As String = ReadLastLines(3, System.Text.Encoding.UTF8, LogFileStream)
' Do something with the last three lines
MsgBox(LastThreeLines)
End Using
请注意,我没有测试过这段代码,我确信它可以改进。它也可能不适用于所有编码,但听起来它应该比您当前的解决方案更好,并且它适用于您的情况。
编辑:另外,为了回答您的问题,IO 操作通常应该异步执行以避免阻塞 UI。您可以使用任务或 BackgroundWorker 来执行此操作。它可能不会使它更快,但它会使您的应用程序更具响应性。最好在任务开始之前表明正在加载某些内容。
如果您知道何时写入文件,则可以设置一个标志以开始读取,然后在读取最后一行时取消设置。如果它没有改变,就没有理由一遍又一遍地阅读它。
推荐阅读
- android - 在 Webview 中获取位置而不显示域
- c# - 带有 SQL 的并行 ForEach 插入 c#
- neo4j - 如何从 neo4j 中的 apoc.path.subgraphall 返回每种关系类型的计数?
- c - strcmp() 不适用于 char *array 和 char const 比较
- xcode - Swift 操场在我打字时运行。如何改变它?
- visual-studio-code - “未安装 Flutter Code 扩展”,但已安装数月
- python - 如何使用 pip 安装来自 github 的 egg 文件,而不是在当前目录中,而是在 conda env 目录中?
- scala - 长路径类(输入行太大)
- neo4j - 在 Neo4j 浏览器上找不到“行”选项卡
- xamarin - CustomRenderer 未应用