.net 后台线程锁定UI中的大量磁盘I/O
在针对230MB数据库的.net 后台线程锁定UI中的大量磁盘I/O,.net,multithreading,backgroundworker,async-await,.net,Multithreading,Backgroundworker,Async Await,在针对230MB数据库的SqlCeEngine.Upgrade()调用过程中,我得到了大量的磁盘I/O—正如预期的那样 问题是,即使它在BackgroundWorker线程中运行,它仍然会锁定UI。我尝试了Async/Await但没有成功。(FWIW,如果时间允许,我将升级代码。) 我要用这个把头发拔出来。我需要在运行时显示一个不确定的进度条,但不管我做什么,进度条都会在中途冻结。我能想到的唯一解决方案是在一个完全独立的过程中进行升级,这当然是相当困难的。如果有必要,我会的 下面是摘录的代码 有
SqlCeEngine.Upgrade()
调用过程中,我得到了大量的磁盘I/O—正如预期的那样
问题是,即使它在BackgroundWorker
线程中运行,它仍然会锁定UI。我尝试了Async/Await
但没有成功。(FWIW,如果时间允许,我将升级代码。)
我要用这个把头发拔出来。我需要在运行时显示一个不确定的进度条,但不管我做什么,进度条都会在中途冻结。我能想到的唯一解决方案是在一个完全独立的过程中进行升级,这当然是相当困难的。如果有必要,我会的
下面是摘录的代码
有什么想法吗
Friend Class MainForm
Private Sub itmRestore_Click(Sender As Object, e As EventArgs) Handles itmRestore.Click
If Utils.MsgQuestion(Bl.Messages.DB_RESTORE_PROMPT) = MsgBoxResult.Yes Then
ofdRestore.InitialDirectory = Bl.Registry.DownloadFolder
ofdRestore.FileName = String.Empty
If ofdRestore.ShowDialog = Windows.Forms.DialogResult.OK Then
prgCopy.SourcePath = ofdRestore.FileName
prgCopy.TargetPath = Db.Utils.DatabasePath
prgCopy.ActionText = "Restoring the database..."
Me.Cursor = Cursors.WaitCursor
bgwBackup.RunWorkerAsync(Bl.BackgroundJobs.RestoreDatabase)
End If
End If
End Sub
Private Sub bgwBackup_DoWork(Sender As Object, e As DoWorkEventArgs) Handles bgwBackup.DoWork
bgwStartup.ReportProgress(-1, "Loading...")
Select Case DirectCast(e.Argument, Bl.BackgroundJobs)
Case Bl.BackgroundJobs.BackupDatabase : Bl.Jobs.Database.BackupDatabase(prgCopy, e)
Case Bl.BackgroundJobs.RestoreDatabase : Bl.Jobs.Database.RestoreDatabase(prgCopy, e)
Case Bl.BackgroundJobs.CheckDatabaseVersion : Bl.Jobs.Database.CheckDatabaseVersion(e)
Case Bl.BackgroundJobs.UpgradeDatabase : Bl.Jobs.Database.UpgradeDatabase(e)
Case Bl.BackgroundJobs.UpdateDatabaseSchema : Bl.Jobs.Database.UpdateDatabaseSchema(Sender, e)
End Select
End Sub
Private Sub bgwBackup_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles bgwBackup.ProgressChanged
UpdateProgress(e.ProgressPercentage, e.UserState)
End Sub
Private Sub bgwBackup_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles bgwBackup.RunWorkerCompleted
Dim eNextJob As Bl.BackgroundJobs
Dim oManager As Bl.Jobs.Manager
oManager = e.Result
If e.Error Is Nothing Then
Select Case oManager.CurrentJob
Case Bl.BackgroundJobs.BackupDatabase
eNextJob = Bl.BackgroundJobs.None
UpdateProgress(0)
Utils.MsgInformation(Bl.Messages.DB_BACKUP_SUCCESS.ToFormat(vbCrLf, sfdBackup.FileName))
Case Bl.BackgroundJobs.RestoreDatabase
eNextJob = Bl.BackgroundJobs.CheckDatabaseVersion
UpdateProgress(0)
Utils.MsgInformation(Bl.Messages.DB_RESTORE_SUCCESS)
Case Bl.BackgroundJobs.CheckDatabaseVersion
If oManager.Continue Then
eNextJob = Bl.BackgroundJobs.UpdateDatabaseSchema
Else
eNextJob = Bl.BackgroundJobs.UpgradeDatabase
Utils.MsgInformation(Bl.Messages.DB_UPGRADE_NOTICE)
UpdateProgress(-1, "Upgrading the database...")
End If
Case Bl.BackgroundJobs.UpgradeDatabase
eNextJob = Bl.BackgroundJobs.UpdateDatabaseSchema
UpdateProgress(0)
Utils.MsgInformation(Bl.Messages.DB_UPGRADE_SUCCESS)
Case Bl.BackgroundJobs.UpdateDatabaseSchema
eNextJob = Bl.BackgroundJobs.None
End Select
Else
Utils.MsgCritical(Bl.Messages.DB_RESTORE_ERROR.ToFormat(vbCrLf, e.Error.ToString))
eNextJob = Bl.BackgroundJobs.None
End If
If eNextJob = Bl.BackgroundJobs.None Then
UpdateProgress(0, "Ready")
txtLastName.Focus()
Me.Cursor = Cursors.Default
Else
bgwBackup.RunWorkerAsync(eNextJob)
End If
End Sub
Private Sub UpdateProgress(ProgressPercentage As Integer)
UpdateProgress(ProgressPercentage, String.Empty)
End Sub
Private Sub UpdateProgress(ProgressPercentage As Integer, StatusText As String)
If ProgressPercentage = -1 Then
If prgProgress.Style <> ProgressBarStyle.Marquee Then
prgProgress.Style = ProgressBarStyle.Marquee
prgProgress.Value = 0
End If
Else
prgProgress.Style = ProgressBarStyle.Blocks
prgProgress.Value = Min(ProgressPercentage, 100)
End If
If Trim(StatusText).Length > 0 Then
lblStatus.Text = StatusText
End If
End Sub
End Class
Namespace Bl
Namespace Jobs
Friend Class Manager
Public CurrentJob As BackgroundJobs
Public [Continue] As Boolean
Public Sub New(CurrentJob As BackgroundJobs, [Continue] As Boolean)
Me.CurrentJob = CurrentJob
Me.Continue = [Continue]
End Sub
End Class
Friend Class Database
Public Shared Sub CheckDatabaseVersion(e As DoWorkEventArgs)
e.Result = New Manager(e.Argument, Db.Utils.CheckDatabaseVersion)
End Sub
Public Shared Sub UpgradeDatabase(e As DoWorkEventArgs)
Db.Utils.UpgradeDatabase()
e.Result = New Manager(e.Argument, True)
End Sub
Public Shared Sub UpdateDatabaseSchema(Worker As BackgroundWorker, e As DoWorkEventArgs)
If Db.Versioning.SchemaVersionStatus = Db.Enums.SchemaVersionStates.Newer Then
Throw New ApplicationException("An incompatible database version has been detected. A newer version of Matrix is required.")
Else
Db.Versioning.UpdateSchema(Worker, e)
e.Result = New Manager(e.Argument, True)
End If
End Sub
Public Shared Sub BackupDatabase(Copier As ProgressCopy.ProgressCopy, e As DoWorkEventArgs)
Copier.Start()
e.Result = New Manager(e.Argument, True)
End Sub
Public Shared Sub RestoreDatabase(Copier As ProgressCopy.ProgressCopy, e As DoWorkEventArgs)
Db.Utils.ArchiveDatabase()
Copier.Start()
e.Result = New Manager(e.Argument, True)
End Sub
End Class
End Namespace
Friend Enum BackgroundJobs
None
UpgradeDatabase
UpdateDatabaseSchema
ValidateCityCode
CheckSubscription
CheckOptionsWizard
CheckDatabaseVersion
BackupDatabase
RestoreDatabase
End Enum
End Namespace
Namespace Db
Friend Class Utils
Public Shared Sub UpgradeDatabase()
Dim _
sSource,
sTarget As String
sSource = ArchiveDatabase()
sTarget = DatabasePath
Try
With New SqlCeEngine(SqlCe.Connection.ConnectionString(sSource))
.Upgrade(SqlCe.Connection.ConnectionString(sTarget))
End With
Catch ex As Exception
Throw New ApplicationException(ex.Message, ex)
End Try
End Sub
Public Shared Function ArchiveDatabase() As String
Dim oFile As FileInfo
Dim _
sTarget,
sNow As String
Dim _
oSource, _
oTarget _
As DirectoryInfo
Do
sNow = Now.ToFileNameString(DateStringOptions.IncludeTime)
oSource = New DirectoryInfo(DatabaseFolder)
sTarget = Path.Combine(oSource.FullName, "Deleted {0}".ToFormat(sNow))
oTarget = New DirectoryInfo(sTarget)
Loop While oTarget.Exists
oTarget.Create()
For Each oFile In oSource.GetFiles
oFile.MoveTo(Path.Combine(oTarget.FullName, oFile.Name))
Next
ArchiveDatabase = Path.Combine(oTarget.FullName, Db.Utils.DatabaseFile)
End Function
End Class
End Namespace
Friend类MainForm
私有子itmRestore\u单击(发件人作为对象,e作为事件参数)处理itmRestore。单击
如果Utils.MsgQuestion(Bl.Messages.DB_RESTORE_PROMPT)=MsgBoxResult.Yes则
ofdRestore.InitialDirectory=Bl.Registry.DownloadFolder
ofdrose.FileName=String.Empty
如果ofdRestore.ShowDialog=Windows.Forms.DialogResult.OK,则
prgCopy.SourcePath=ofdRestore.FileName
prgCopy.TargetPath=Db.Utils.DatabasePath
prgCopy.ActionText=“正在恢复数据库…”
Me.Cursor=Cursors.WaitCursor
bgwBackup.RunWorkerAsync(Bl.BackgroundJobs.RestoreDatabase)
如果结束
如果结束
端接头
私有子bgwBackup_DoWork(发送方作为对象,e作为DoWorkEventArgs)处理bgwBackup.DoWork
bgwStartup.ReportProgress(-1,“加载…”)
选择案例DirectCast(例如参数,Bl.BackgroundJobs)
案例Bl.BackgroundJobs.backupdatebase:Bl.Jobs.Database.backupdatebase(prgCopy,e)
案例Bl.BackgroundJobs.RestoreDatabase:Bl.Jobs.Database.RestoreDatabase(prgCopy,e)
案例Bl.BackgroundJobs.CheckDatabaseVersion:Bl.Jobs.Database.CheckDatabaseVersion(e)
案例Bl.BackgroundJobs.UpgradeDatabase:Bl.Jobs.Database.UpgradeDatabase(e)
案例Bl.BackgroundJobs.UpdateDatabaseSchema:Bl.Jobs.Database.UpdateDatabaseSchema(发件人,e)
结束选择
端接头
私有子bgwBackup\u ProgressChanged(发送方作为对象,e作为ProgressChangedEventArgs)处理bgwBackup.ProgressChanged
UpdateProgress(e.ProgressPercentage,e.UserState)
端接头
私有子bgwBackup\u RunWorkerCompleted(发送方作为对象,e作为RunWorkerCompletedEventArgs)处理bgwBackup.RunWorkerCompleted
Dim eNextJob作为Bl.BackgroundJobs
将oManager设置为Bl.Jobs.Manager
oManager=e.结果
如果e.错误不算什么,那么
选择案例oManager.CurrentJob
案例Bl.BackgroundJobs.BackUpdatebase
eNextJob=Bl.BackgroundJobs.None
UpdateProgress(0)
MsgInformation(Bl.Messages.DB_BACKUP_SUCCESS.ToFormat(vbCrLf,sfdBackup.FileName))
案例Bl.BackgroundJobs.RestoreDatabase
eNextJob=Bl.BackgroundJobs.CheckDatabaseVersion
UpdateProgress(0)
Utils.MsgInformation(Bl.Messages.DB\u RESTORE\u SUCCESS)
案例Bl.BackgroundJobs.CheckDatabaseVersion
如果是oManager,则继续
eNextJob=Bl.BackgroundJobs.UpdateDatabaseSchema
其他的
eNextJob=Bl.BackgroundJobs.upgrade数据库
Utils.MsgInformation(Bl.Messages.DB\u升级通知)
UpdateProgress(-1,“升级数据库…”)
如果结束
案例Bl.BackgroundJobs.Upgrade数据库
eNextJob=Bl.BackgroundJobs.UpdateDatabaseSchema
UpdateProgress(0)
Utils.MsgInformation(Bl.Messages.DB\u升级成功)
案例Bl.BackgroundJobs.UpdateDatabaseSchema
eNextJob=Bl.BackgroundJobs.None
结束选择
其他的
Utils.msgccritical(Bl.Messages.DB_RESTORE_ERROR.ToFormat(vbCrLf,e.ERROR.ToString))
eNextJob=Bl.BackgroundJobs.None
如果结束
如果eNextJob=Bl.BackgroundJobs.None,则
UpdateProgress(0,“就绪”)
txtLastName.Focus()
Me.Cursor=Cursors.Default
其他的
bgwBackup.RunWorkerAsync(eNextJob)
如果结束
端接头
私有子UpdateProgress(ProgressPercentage作为整数)
UpdateProgress(ProgressPercentage,String.Empty)
端接头
私有子UpdateProgress(ProgressPercentage为整数,StatusText为字符串)
如果ProgressPercentage=-1,则
如果prgProgress.Style ProgressBarStyle.Marquee,则
prgProgress.Style=ProgressBarStyle.Marquee
prgProgress.Value=0
如果结束
其他的
prgProgress.Style=ProgressBarStyle.Blocks
prgProgress.Value=Min(进度百分比,100)
如果结束
如果修剪(StatusText).Length>0,则
lblStatus.Text=状态文本
如果结束
端接头
末级
名称空间Bl
命名空间作业
朋友班经理
公共作业作为背景作业
公共[继续]为布尔值
Public Sub New(当前作业作为背景作业,[继续]作为布尔值)
Me.CurrentJob=CurrentJob
Me.Continue=[继续]
端接头
末级
好友类数据库
公共共享子检查数据库版本(e作为DoWorkEventArgs)
e、 结果=新管理器(e.Argument,Db.Utils.CheckDatabaseVersion)
端接头
公共共享子升级数据库(如DoWorkEventArgs)
Db.Utils.UpgradeDatabase()
e、 结果=新管理器(例如参数,True)
端接头
公共共享子UpdateDatabaseSchema(Worker作为BackgroundWorker,e作为DoWorkEventArgs)
如果Db.Versioning.schemaversationstatus=Db.Enums.schemaversationstates.Newer,则
抛出新的ApplicationException(“检测到不兼容的数据库版本。需要更新版本的Matrix。”)
其他的
Db.Versioning.UpdateSchema(Worker,e)
e、 结果=新管理器(例如参数,True)
如果结束
端接头
公共共享子备份数据库(复印机为ProgressCopy.ProgressCopy,e为DoWorkEventArgs)
复印机启动
e、 结果=新管理器(例如参数,True)
端接头
公共共享子恢复数据库(复印机为ProgressCo