将这个放到后台工作人员身上会停止这个问题吗

Will Putting This Onto A background Worker Stop This Issue

几天来我一直在尝试解决这个问题,但没有成功。我知道我已经创建了另一个与此问题相关的 post 但不确定我是否应该继续使用另一个 post 而不是创建一个新的,因为我对 SO 的工作方式仍然很陌生所以如果我走错了路。

Objective
从磁盘读取一个文本文件,将内容输出到一个文本框,这样我就可以从中提取最后 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

我的解决方案不需要 reader。读取将手动完成。

接下来,您需要读取流的最后 n 行(在您的示例中为 3 行)。当您只对末尾的几行感兴趣时读取整个文件是低效的。相反,您可以从末尾开始阅读,直到读到三个(或任意数量的)行分隔符(基于 this answer):

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

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