【问题标题】:Vb.net Winform Update GUI between Syncronous Sql calls同步 Sql 调用之间的 Vb.net Winforms 更新 UI
【发布时间】:2011-10-06 05:39:28
【问题描述】:

topic 大家好,我为此进行了大量搜索,但找不到真正适合这种情况的结果。

我有一个(目前)用 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 之后)。但是程序直接跳过了这一步(底部的案例 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

【问题讨论】:

  • 在涉及 WinForms 时,后台工作人员是最明显的路线。您在让应用程序等待时遇到了什么困难?
  • 后台工作者的重点是应用程序不会等待。如果这样做,它不会解决您的任何问题。

标签: sql vb.net winforms user-interface synchronous


【解决方案1】:

不确定为什么您的后台线程不适合您,但如果您因某种原因遇到问题,您可以尝试此解决方案。对于您需要的东西可能有点矫枉过正,但它会起作用。

因此,您可能想要研究的是使用 Syncevents 进程和队列的同步模式。如果您要将所有项目弹出到队列 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 

}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-20
    • 2010-09-14
    • 2011-04-04
    • 2012-01-31
    • 1970-01-01
    • 2014-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多