使用线程冻结 UI
Freeze UI with Threads
我有一个计时器来制作线程:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
'ListBox2.Items.Add("Hilo")
hiloCertificador1 = New Thread(AddressOf crearObjeto1)
hiloCertificador1.IsBackground = True
hiloCertificador1.Start()
End Sub
Public Sub crearObjeto1()
UpdateList()
End Sub
Private Delegate Sub UpdateListDelegate()
Private Sub UpdateList()
If Me.InvokeRequired Then
Me.BeginInvoke(New UpdateListDelegate(AddressOf UpdateList))
Else
Dim conn As New SqlConnection(parametrosCon)
Dim cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Dim da As New SqlClient.SqlDataAdapter(cmd)
cmd.Connection.Open()
da.SelectCommand = cmd
da.Fill(dataSet, "Query")
For Each fila As DataRow In dataSet.Tables(0).Rows
cmd = New SqlCommand("UPDATE COLA SET enviado = 1 WHERE DOCENTRY = (@docEntry) AND TIPO = (@tipodoc)", conn)
cmd.Parameters.AddWithValue("@docEntry", fila("docentry"))
cmd.Parameters.AddWithValue("@tipodoc", fila("tipo"))
cmd.ExecuteNonQuery()
Dim factura As New FacturaCerificacion(fila("docentry"), fila("tipo"))
Next
cmd.Connection.Close()
dataSet.Tables("Query").Clear()
End If
End Sub
定时器有一个 4000 间隔,但是当一个线程开始冻结我的 UI,我认为是因为进程太大或查询但我需要在不冻结的情况下完成它。
评论是正确的,我会为你描述其中提示的问题
使用 System.Windows.Forms.Timer
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
这将 运行 在 UI 线程上,并且只适用于做 UI 事情(即便如此,我不确定你是否真的能为它提出理由System.Threading.Timer
)
创建一个新的 System.Threading.Thread
hiloCertificador1 = New Thread(AddressOf crearObjeto1)
hiloCertificador1.IsBackground = True
hiloCertificador1.Start()
现在 运行 关闭 UI,并且是 Timer.Tick 的全部内容。因此,您已勾选 UI,然后在 UI 之外创建了一个新线程。这很奇怪
调用Sub调用Sub
Public Sub crearObjeto1()
UpdateList()
End Sub
Private Sub UpdateList()
' do stuff
End Sub
冗余应该是不言而喻的
做非UI的事情但遵循Control.InvokeRequired/BeginInvoke
模式
Private Delegate Sub UpdateListDelegate()
Private Sub UpdateList()
If Me.InvokeRequired Then
Me.BeginInvoke(New UpdateListDelegate(AddressOf UpdateList))
Else
' looks like a bunch of non-UI stuff
End If
End Sub
此模式用于在 UI 上执行操作,但该块中似乎没有 UI 代码。
不使用 Using
以确保正确处理 IDisposable
个对象
Dim conn As New SqlConnection(parametrosCon)
Dim cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Dim da As New SqlClient.SqlDataAdapter(cmd)
' do stuff
cmd.Connection.Close()
DataSet.Tables("Query").Clear()
与您当前的问题无关,但也很重要。
解决方案
因此,虽然这看起来是一项崇高的努力,但您似乎在 UI 之间来回走动,而且并非无缘无故,或者更准确地说,制造了 none 存在的问题一些过度工程。整个事情可以通过一些小的改变来简化
使用System.Threading.Timer
Dim Timer2 As New System.Threading.Timer(Sub() UpdateList(), Nothing, -1, -1)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' done in Form_Load if your Timer1 is Enabled in designer
' or can be done in a Button.Click, or however you had enabled Timer1
Timer2.Change(2000, 4000) ' this will enable Timer after 2 seconds, then will tick every 4 seconds
'Timer2.Change(-1, -1) ' this is how it's disabled
End Sub
只需调用此方法,并使用Using
正确处理您的数据库对象。添加了 Sub DoUiStuff()
模式的正确实现方式
Private Sub UpdateList()
Timer2.Change(-1, -1)
Using conn As New SqlConnection(parametrosCon)
conn.Open()
Using cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Using da As New SqlClient.SqlDataAdapter(cmd)
da.SelectCommand = cmd
da.Fill(DataSet, "Query")
For Each fila As DataRow In DataSet.Tables(0).Rows
Using cmdInner = New SqlCommand("UPDATE COLA SET enviado = 1 WHERE DOCENTRY = (@docEntry) AND TIPO = (@tipodoc)", conn)
cmd.Connection.Open()
cmd.Parameters.AddWithValue("@docEntry", fila("docentry"))
cmd.Parameters.AddWithValue("@tipodoc", fila("tipo"))
cmd.ExecuteNonQuery()
Dim factura As New FacturaCerificacion(fila("docentry"), fila("tipo"))
End Using
Next
End Using
End Using
DoUiStuff(arguments) ' for example, if you need to update a GridView
DataSet.Tables("Query").Clear()
End Using
End Sub
Private Sub DoUiStuff(arguments As Whatever)
If Me.InvokeRequired() Then
Me.Invoke(New Action(Of Whatever)(AddressOf DoUiStuff), arguments)
Else
' do UI stuff with arguments
End If
Timer2.Change(2000, -1)
End Sub
最后,为了不自相矛盾,我将添加 Dispose 方法来处理 Timer。默认情况下,此 Sub 将位于您的 Form.Designer.vb 文件中,您可以将其保留在那里或在添加后将其移至 Form.vb。
'Form overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing Then
components?.Dispose()
Timer2?.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
我有一个计时器来制作线程:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
'ListBox2.Items.Add("Hilo")
hiloCertificador1 = New Thread(AddressOf crearObjeto1)
hiloCertificador1.IsBackground = True
hiloCertificador1.Start()
End Sub
Public Sub crearObjeto1()
UpdateList()
End Sub
Private Delegate Sub UpdateListDelegate()
Private Sub UpdateList()
If Me.InvokeRequired Then
Me.BeginInvoke(New UpdateListDelegate(AddressOf UpdateList))
Else
Dim conn As New SqlConnection(parametrosCon)
Dim cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Dim da As New SqlClient.SqlDataAdapter(cmd)
cmd.Connection.Open()
da.SelectCommand = cmd
da.Fill(dataSet, "Query")
For Each fila As DataRow In dataSet.Tables(0).Rows
cmd = New SqlCommand("UPDATE COLA SET enviado = 1 WHERE DOCENTRY = (@docEntry) AND TIPO = (@tipodoc)", conn)
cmd.Parameters.AddWithValue("@docEntry", fila("docentry"))
cmd.Parameters.AddWithValue("@tipodoc", fila("tipo"))
cmd.ExecuteNonQuery()
Dim factura As New FacturaCerificacion(fila("docentry"), fila("tipo"))
Next
cmd.Connection.Close()
dataSet.Tables("Query").Clear()
End If
End Sub
定时器有一个 4000 间隔,但是当一个线程开始冻结我的 UI,我认为是因为进程太大或查询但我需要在不冻结的情况下完成它。
评论是正确的,我会为你描述其中提示的问题
使用 System.Windows.Forms.Timer
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
这将 运行 在 UI 线程上,并且只适用于做 UI 事情(即便如此,我不确定你是否真的能为它提出理由System.Threading.Timer
)
创建一个新的 System.Threading.Thread
hiloCertificador1 = New Thread(AddressOf crearObjeto1)
hiloCertificador1.IsBackground = True
hiloCertificador1.Start()
现在 运行 关闭 UI,并且是 Timer.Tick 的全部内容。因此,您已勾选 UI,然后在 UI 之外创建了一个新线程。这很奇怪
调用Sub调用Sub
Public Sub crearObjeto1()
UpdateList()
End Sub
Private Sub UpdateList()
' do stuff
End Sub
冗余应该是不言而喻的
做非UI的事情但遵循Control.InvokeRequired/BeginInvoke
模式
Private Delegate Sub UpdateListDelegate()
Private Sub UpdateList()
If Me.InvokeRequired Then
Me.BeginInvoke(New UpdateListDelegate(AddressOf UpdateList))
Else
' looks like a bunch of non-UI stuff
End If
End Sub
此模式用于在 UI 上执行操作,但该块中似乎没有 UI 代码。
不使用 Using
以确保正确处理 IDisposable
个对象
Dim conn As New SqlConnection(parametrosCon)
Dim cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Dim da As New SqlClient.SqlDataAdapter(cmd)
' do stuff
cmd.Connection.Close()
DataSet.Tables("Query").Clear()
与您当前的问题无关,但也很重要。
解决方案
因此,虽然这看起来是一项崇高的努力,但您似乎在 UI 之间来回走动,而且并非无缘无故,或者更准确地说,制造了 none 存在的问题一些过度工程。整个事情可以通过一些小的改变来简化
使用System.Threading.Timer
Dim Timer2 As New System.Threading.Timer(Sub() UpdateList(), Nothing, -1, -1)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' done in Form_Load if your Timer1 is Enabled in designer
' or can be done in a Button.Click, or however you had enabled Timer1
Timer2.Change(2000, 4000) ' this will enable Timer after 2 seconds, then will tick every 4 seconds
'Timer2.Change(-1, -1) ' this is how it's disabled
End Sub
只需调用此方法,并使用Using
正确处理您的数据库对象。添加了 Sub DoUiStuff()
模式的正确实现方式
Private Sub UpdateList()
Timer2.Change(-1, -1)
Using conn As New SqlConnection(parametrosCon)
conn.Open()
Using cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Using da As New SqlClient.SqlDataAdapter(cmd)
da.SelectCommand = cmd
da.Fill(DataSet, "Query")
For Each fila As DataRow In DataSet.Tables(0).Rows
Using cmdInner = New SqlCommand("UPDATE COLA SET enviado = 1 WHERE DOCENTRY = (@docEntry) AND TIPO = (@tipodoc)", conn)
cmd.Connection.Open()
cmd.Parameters.AddWithValue("@docEntry", fila("docentry"))
cmd.Parameters.AddWithValue("@tipodoc", fila("tipo"))
cmd.ExecuteNonQuery()
Dim factura As New FacturaCerificacion(fila("docentry"), fila("tipo"))
End Using
Next
End Using
End Using
DoUiStuff(arguments) ' for example, if you need to update a GridView
DataSet.Tables("Query").Clear()
End Using
End Sub
Private Sub DoUiStuff(arguments As Whatever)
If Me.InvokeRequired() Then
Me.Invoke(New Action(Of Whatever)(AddressOf DoUiStuff), arguments)
Else
' do UI stuff with arguments
End If
Timer2.Change(2000, -1)
End Sub
最后,为了不自相矛盾,我将添加 Dispose 方法来处理 Timer。默认情况下,此 Sub 将位于您的 Form.Designer.vb 文件中,您可以将其保留在那里或在添加后将其移至 Form.vb。
'Form overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing Then
components?.Dispose()
Timer2?.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub