首页 > 解决方案 > 将其放在后台工作人员上会停止此问题

问题描述

我已经尝试解决这个问题好几天了,但没有任何成功。我知道我已经创建了另一个与此问题相关的帖子,但不确定我是否应该继续另一个帖子而不是创建一个新帖子,因为我对 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()

标签: vb.netbackgroundworker

解决方案


首先,您应该使用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 来执行此操作。它可能不会使它更快,但它会使您的应用程序更具响应性。最好在任务开始之前表明正在加载某些内容。

如果您知道何时写入文件,则可以设置一个标志以开始读取,然后在读取最后一行时取消设置。如果它没有改变,就没有理由一遍又一遍地阅读它。


推荐阅读