如何实现不同BackgroundWorker实例访问同一文件时不锁定源文件
问题核心原因
你遇到的多设备同时拷贝就异常终止的问题,和BackgroundWorker本身无关,是文件流打开参数错误、异常捕获逻辑有遗漏导致的:
- 你把文件流初始化的代码放在了
Try块之外,一旦流打开失败(触发文件占用异常),这部分异常不会被Catch捕获,后台线程会直接抛出未处理异常触发RunWorkerCompleted流程,同时你在RunWorkerCompleted里加了On Error Resume Next,直接吞掉了异常提示,所以看不到任何报错。 - 源文件打开时不必要地申请了读写权限,且共享模式设置不合理,导致第一个进程打开文件后,其余进程无法再获取到匹配的权限打开文件,直接打开失败。
修正后的代码
Private w101 As String Private SaveDir As String Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork Dim parts As String() = w101.Split(New Char() {"\"c}) Dim filename As String = parts(parts.Count - 1) ' 目标路径用Path.Combine拼接,避免斜杠错误 Dim targetPath As String = IO.Path.Combine(SaveDir, filename) Dim lngLen As Long = 0 Dim lnglenres As Long = 0 Dim byteBuffer(4096) As Byte Dim intBytesRead As Integer Try ' 源文件只申请读权限,共享模式设置为允许其他进程读写,避免锁死文件 Using streamRead As New System.IO.FileStream(w101, System.IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.ReadWrite) lngLen = streamRead.Length ' 处理空文件场景,避免除零错误 If lngLen = 0 Then e.Result = "空文件传输完成" Exit Sub End If lnglenres = CLng(Math.Ceiling(lngLen / 1048576)) setLabelTxt1("STATUS : (0/" & lnglenres.ToString & " MB) Transferred.", KITTASK_media1progress) ' 目标文件流也用Using包裹,异常时自动释放 Using streamWrite As New System.IO.FileStream(targetPath, IO.FileMode.Create, IO.FileAccess.Write, IO.FileShare.None) While (streamWrite.Length < lngLen) If BackgroundWorker1.CancellationPending Then e.Cancel = True Exit While End If BackgroundWorker1.ReportProgress(CLng(streamRead.Position / lngLen * 100)) setLabelTxt1("STATUS : (" & CLng(streamRead.Position / 1048576).ToString & " MB / " & lnglenres.ToString & " MB) Transferred.", KITTASK_media1progress) intBytesRead = streamRead.Read(byteBuffer, 0, 4096) streamWrite.Write(byteBuffer, 0, intBytesRead) End While streamWrite.Flush() End Using End Using Catch ex As Exception ' 这里可以记录异常日志,或者弹窗提示,不会再漏报错 MessageBox.Show("ERROR: " & ex.Message) e.Cancel = True End Try End Sub Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged On Error Resume Next ProgressBar1.Value = e.ProgressPercentage End Sub Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted On Error Resume Next If e.Cancelled = True Then MsgBox("File transfer cancelled by user.") ProgressBar1.Value = 0 KITTASK_media1progress.Text = "STATUS : Ready" Else MsgBox("1 ok") ProgressBar1.Value = 0 KITTASK_media1progress.Text = "STATUS : Ready" End If End Sub
关键修改说明
- 把所有文件流操作放到
Try块内部,同时用Using语句包裹流,不管运行是否异常都会自动释放文件句柄,不会残留锁。 - 源文件打开权限从
ReadWrite改为Read,不需要写权限就不要申请,减少冲突概率。 - 源文件共享模式从
FileShare.Read改为FileShare.ReadWrite,允许其他进程同时读写该文件,不会被当前进程锁死。 - 修复了空文件、文件长度计算错误可能导致的除零、进度计算错误问题。
- 用
Path.Combine拼接目标路径,避免手动拼接斜杠导致的路径错误。
内容的提问来源于stack exchange,提问作者Walter P Steiner
相关产品推荐
相关产品推荐

