会将其放到后台工作人员中来解决此问题

问题描述

我已经尝试解决了好几天,但没有成功。我知道我已经创建了另一个与此问题相关的帖子,但是不确定是否应该继续其他帖子而不是创建一个新帖子,因为如果我以错误的方式进行操作,那么我对SO的工作原理还是很陌生的

目标
从磁盘读取一个文本文件,将内容输出到文本框,这样我就可以从中提取最后三行。这是我想到的唯一方法

该文本文件被另一个正在运行的程序不断更新,但是即使它正在使用中,我仍然可以读取它,但是无法对其进行写入。

我正在通过一个计时器进行探测,该计时器每1秒滴答一次,以获取最新信息。

正在解决此问题...
我注意到一段时间后我的应用程序变得缓慢,当我尝试在屏幕上移动它或调整其大小并且cpu使用率开始攀升到33%以上时,这是显而易见的

我的思考过程
由于读取的文件是连续的,因此我认为我可以将其移动到BackgroundWorker上,根据我的理解,它将把它放在另一个线程上并减轻主GUI的负担。

在这棵树上叫错了树吗?

在开始学习所有有关使用BackgroundWorker的教科书之前,我正在与更高级的用户联系。

这是我用来读取txt文件并将其输出到文本框的代码。我没有包含提取最后三行的代码,因为我不认为这是造成问题的原因。

我认为问题是因为我一直在使用计时器不断地每秒对源文件进行探测,但并非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)。当您只对最后几行感兴趣时,读取整个文件效率很低。相反,您可以从头开始阅读,直到达到三个(或任意数量的)行分隔符(基于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,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,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,Buffer.Length)
    Return Encoding.GetString(Buffer)
End Function

用法:

Using LogFileStream As New FileStream(strLogFilePath,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执行此操作。它可能不会使其速度更快,但是它将使您的应用程序具有更高的响应速度。最好在任务开始之前指示正在加载某些东西。

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