主题大家好,我为此搜索了很多,但无法找到真正符合这种情况的结果。
我有一个用VB.Net编写的(当前)单线程应用程序主要的处理步骤是树视图控件中的12个已检查列表项。程序依次执行步骤(函数和存储过程)并在继续之前检查结果。
步骤(大大简化)是: 1.从txt文件导入数据 2.批量将其插入数据库 3.做一些处理 4.使用该数据从链接服务器的复杂联接中检索网络中的更多相关数据。 5.使用结果更新本地数据库 6.做更多的处理 7.将最终结果插入到不同服务器上的不同数据库中。
我不会理解必须以这种方式完成的所有原因(公司内部的不同服务器所有者,服务器之间缺乏信任,只读访问某些数据库等)但问题是步骤4
根据(外部)服务器上的处理负载和导入文件中的数据量,此步骤可能需要1-1 / 2小时才能执行。由于这是一个单线程应用程序,因此gui会冻结等待从链接服务器检索数据。
除了桌面上的灰色块(由于没有gui更新),程序运行完美。
尝试的解决方案: 1)我尝试了一个计时器的建议来刷新表单但没有成功。
2)我尝试过使用后台工作进程,但在程序继续之前无法让应用程序等待结果。
3)我也尝试了不同的线程示例但没有成功。所有其他步骤都很快完成,gui永远不会释放,但我不反对在这个应用程序中线程化所有的SQL调用。
在我这段时间里,这是我第一次无法在网上找到解决方案,而且以前从未需要发布,所以非常感谢您对此事的帮助。
- 修改
这是我试过的: 感谢您的快速回复。
我使用了此处描述的过程:http://midnightprogrammer.net/post/Using-Background-Worker-in-C.aspx(将其转换为vb.net后)。但是程序正好超过了步骤(底部的Case Node6)。
Imports System.Runtime.InteropServices
Imports System.Threading
Imports System.ComponentModel
Private Sub bgw_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs)
'Report progress bar change
progress.Value = e.ProgressPercentage
End Sub
Private Sub bgw_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
If (e.Cancelled) Then
WriteStatus("Operation Cancelled")
bRetval = False
Else
WriteStatus("Operation Completed")
bRetval = True
End If
End Sub
'Background worker DoWork method. Here we will perform our heavy duty tasks.
Private Sub bgw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim Sql As String = "Get_ALS_Data"
Dim cmd As New SqlCommand(Sql, ConnLocal)
cmd.CommandTimeout = 9000 ' 2 and a half hours
Try
Dim i As Integer = 0
ConnLocal.Open()
'ImportRowCount = cmd.ExecuteScalar()
Dim dr As SqlDataReader
dr = cmd.ExecuteReader()
While dr.Read()
i = i + 1
'report to the backgroundworkerprogress.changed event of the background worker class
bgw.ReportProgress(i)
Thread.Sleep(1)
'Call and check if the cancellation of the operation is pending. If returned true
'DoWorkEventArgs object cancels the operation.
If bgw.CancellationPending Then
e.Cancel = True
Return
End If
ImportRowCount = CInt(dr(0))
End While
Catch ex As Exception
WriteStatus("Get ALS Data error: " & ex.Message)
e.Cancel = True
Return
Finally
ConnLocal.Close()
End Try
WriteStatus("Get ALS Data completed successfully.")
e.Cancel = False
Return
End Sub
Private Sub bgw_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs)
'Report progress bar change
progress.Value = e.ProgressPercentage
End Sub
Private Sub bgw_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
If (e.Cancelled) Then
WriteStatus("Operation Cancelled")
bRetval = False
Else
WriteStatus("Operation Completed")
bRetval = True
End If
End Sub
Private Sub ProcessCheckedNodes(ByVal parentNode As TreeNode)
Dim Success As Boolean = False
For Each childNode As TreeNode In parentNode.Nodes
ProgBar += BarSeg
If ProgBar > 100 Then
ProgBar = 100
End If
pb1.Value = ProgBar
Me.Refresh()
If childNode.Checked Then
Select Case childNode.Name
Case "Node1"
'1. Clear local work tables
SetChild(childNode, "True")
Success = DoCleanup()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
Case "Node2"
'2. Clear Server Intake table
SetChild(childNode, "True")
Success = TruncateInserts("DoCleanUp")
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
Case "Node3"
'3. Load the temp table
SetChild(childNode, "True")
Success = LoadMyTempTable()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
Case "Node4"
'4. Load the data from the temp table to the local database
SetChild(childNode, "True")
Success = BulkCopy_Intake()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
Case "Node5"
'5. Get Intake Dup's
SetChild(childNode, "True")
Success = GetIntakeDups()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
**Case "Node6"
'6. Get the matching data from the ALS database
SetChild(childNode, "True")
'Success = GetALS_Data()
bgw.RunWorkerAsync()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")**
Case "Node7"
'7. Get Core Dup's
SetChild(childNode, "True")
Success = GetCoreDups()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
Case "Node8"
'8. Process
SetChild(childNode, "True")
Success = Process()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
Case "Node9"
'9. Export NotFound
SetChild(childNode, "True")
Success = ExportNotFound()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
Case "Node10"
'10. Move Inserts
SetChild(childNode, "True")
Success = MoveInserts()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
Case "Node11"
'11. Backup
SetChild(childNode, "True")
Success = Backup()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
Case "Node12"
SetChild(childNode, "True")
'Success = LoadDc()
If Success <> True Then
SetChild(childNode, "Error")
Cursor = Cursors.Default
Exit Sub
End If
SetChild(childNode, "False")
Case Else
'Ignore it
End Select
Else
childNode.ImageIndex = 0
childNode.SelectedImageIndex = 0
End If
ProcessCheckedNodes(childNode)
Next
pb1.Value = 100
End Sub**strong text**
Public Function GetALS_Data() As Boolean
'refresh the form while waiting for the ALS data
Dim sAcct As String = ""
Dim Sql As String = "Get_ALS_Data"
Dim cmd As New SqlCommand(Sql, ConnLocal)
cmd.CommandTimeout = 9000 ' 2 and a half hours
Try
ConnLocal.Open()
ImportRowCount = cmd.ExecuteScalar()
Catch ex As Exception
WriteStatus("Get ALS Data error: " & ex.Message)
Return False
Finally
ConnLocal.Close()
End Try
WriteStatus("Get ALS Data completed successfully.")
Return True
End Function
答案 0 :(得分:1)
不确定为什么你的后台线程不适合你,但如果你出于某种原因遇到问题,你可以试试这个解决方案。对于你需要的东西可能有点矫枉过正,但它会起作用。
所以你可能想要研究的是使用Syncevents进程和Queue的同步模式。如果您要将所有项目弹出到队列FIFO模型中并在后台线程中使用它们,您可以执行所有您正在讨论的内容并维护可用的UI,使其可取消等等。这样做的一个好处是除了超时之外没有超时在您的数据库调用或其他项目期间,但不是线程。
我的代码在C#中,但你可以得到图片。您还可以查看有关EventHandle流程的信息:MSDN - EventWaitHandle
我没有测试过这个,因为我刚刚输入了它,但我们在我使用的应用程序上使用这种类型的内部排队模型,它非常适合在后台处理数据并维护响应式UI以便长时间运行和可取消的操作。如果要取消操作,只需设置ExitThreadEvent即可。希望这可能会为您提供一些额外的尝试途径。
创建一个SyncEvent类
public class SyncEvents
{
private EventWaitHandle _newItemEvent;
private EventWaitHandle _exitThreadEvent;
private WaitHandle[] _eventArray;
public SyncEvents()
{
_newItemEvent = new AutoResetEvent(false);
_exitThreadEvent = new ManualResetEvent(false);
_eventArray = new WaitHandle[2];
_eventArray[0] = _newItemEvent;
_eventArray[1] = _exitThreadEvent;
}
public EventWaitHandle ExitThreadEvent
{
get { return _exitThreadEvent; }
}
public EventWaitHandle NewItemEvent
{
get { return _newItemEvent; }
}
public WaitHandle[] EventArray
{
get { return _eventArray; }
}
}
使用Queue对象,您可以创建一个指令类来完成您的工作,并创建一个类似于此类的线程。
public class RunItem
{
public int SequenceToRun { get; set; }
}
public class ItemRunner
{
private SyncEvents _syncEvents;
private Queue<RunItem> _items;
private Thread _processThread;
public ItemRunner(SyncEvents events, Queue<RunItem> items)
{
this._syncEvents = events;
this._items = items;
}
public void Start()
{
this._processThread = new Thread(this.Run);
this._processThread.IsBackground = true;
this._processThread.Start();
}
private void Run()
{
try
{
while (WaitHandle.WaitAny(_syncEvents.EventArray) != 1)
{
RunItem item = null;
lock (this._items)
{
if (this._items.Count > 0)
{
item = this._items.Dequeue();
if (item != null)
this.ProcessItem(item);
this._syncEvents.NewItemEvent.Set();
}
else
{
this._syncEvents.ExitThreadEvent.Set();
}
}
}
}
catch (Exception ex)
{
// do something to log your exception here
// you should have the try catch since you are running in a thread and if it
// throws an exception it could kill your entire app.
}
}
private void ProcessItem(RunItem item)
{
// Do your item processing here.
// You could have a final item that executes the
// this._syncEvents.ExitThreadEvent.Set(); so that it actually will stop waiting
}
}