I have a timer to make a thread:
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
The timer have a 4000 interval, but when a thread start freeze my UI, I think is because the process is so big or the querys but i need make it without freezing.
CodePudding user response:
The comment is correct, and I will describe for you the issues hinted therein
Using a System.Windows.Forms.Timer
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
This will run on the UI thread, and is only appropriate when doing UI things (even then, I am not sure you can really make a case for it over System.Threading.Timer
)
Creating a new System.Threading.Thread
hiloCertificador1 = New Thread(AddressOf crearObjeto1)
hiloCertificador1.IsBackground = True
hiloCertificador1.Start()
This now runs off the UI, and is the entire contents of the Timer.Tick. So you've ticked on the UI then created a new thread off the UI. This is very odd
Calling a Sub to call a Sub
Public Sub crearObjeto1()
UpdateList()
End Sub
Private Sub UpdateList()
' do stuff
End Sub
The redundancy should be self-evident
Doing non-UI stuff but following the Control.InvokeRequired/BeginInvoke
pattern
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
This pattern is used for doing things on the UI but there seems to be no UI code in that block.
Not using Using
to ensure proper disposal of IDisposable
objects
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()
Nongermane to your current issue, but also important to know.
Solution
So although it looks like a noble effort, you seem to be going back and forth between UI and not for no reason at all, or more accurately, creating issues where none existed with some over-engineering. The whole thing can be simplified with some minor changes
Use 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
Just call this method, and use Using
to properly dispose your database objects. Added a Sub DoUiStuff()
which is how the pattern is properly implemented
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
Finally, so I'm not contradicting myself, I'll add the Dispose method to dispose of the Timer. This Sub will be by default in your Form.Designer.vb file which you can leave there or move it to Form.vb once you add to it.
'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