Threads sincronizados
Propósito de este documento:
Mostrar como programar con hilos y como sincronizarlos con SynchronizationContext para
evitar algunos inconvenientes conocidos.
Evitar la ecxepción: “Operación no válida a través de subprocesos: Se
tuvo acceso al control '…' desde un subproceso distinto a aquel en que lo creó.”
Si estás leyendo esto seguramente ya hayas intentado programar
con threads o por lo menos tengas una idea de lo que es.
Supongamos una situación simple.
Debemos crear un programa que dado 2 valores, los sume y muestre el resultado.
Para ello abrimos el Visual Studio. En un proyecto nuevo de Windows Forms
Parte 1 – Una simple suma
Agregar:
3 TextBox (txtVal1, txtVal2, txtResult)
1 Botón (btnCalcular)
Programamos el botón Calcular de la siguiente forma
#Region "Calcular suma"
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles btnCalcular.Click
txtResult.Text =
Val(txtVal1.Text) + Val(txtVal2.Text)
End Sub
#End Region
Esto es realmente sencillo.
Parte 2 – Suma con Thread v1
Ahora lo mismo pero utilizando un thread. Similar al caso anterior,
usaremos un thread para realizar el cálculo y en lugar de mostrarlo en un txtResult vamos
a mostrarlo con un msgbox().
Agregar:
2 TextBox (txtT1Val1, txtT1Val2)
1 Botón (btnT1Calcular)
En el código del formulario agregaremos lo siguiente
#Region "Calcular suma con
thread v1"
'Inicializo
un thread.
Private thT1 As System.Threading.Thread 'New
System.Threading.Thread(AddressOf RealizarCalculo) With {.IsBackground = True}
'Background
determina que es un thread secundario.
'Esto se
pone para que si el thread se encuentra activo ejecutando alguna tarea y
cerramos
'el
formulario principal (que se está ejecutando en el thread primario) el
secundario
'es
también finalizado. En otras palabras, evita que al cerrar el formulario
principal
'se siga
ejecutando el programa
Private Sub btnT1Calcular_Click(sender As Object, e As EventArgs) Handles btnT1Calcular.Click
thT1 = New System.Threading.Thread(AddressOf T1RealizarCalculo) With {.IsBackground = True}
thT1.Start()
End Sub
Private Sub T1RealizarCalculo()
Dim suma As Double = Val(txtT1Val1.Text) + Val(txtT1Val2.Text)
MsgBox(suma)
End Sub
#End Region
Parte 3 – Suma con Thread v2
Igual al anterior pero mostrando el resultado un TextBox en lugar de MsgBox()
Agregar:
3 TextBox (txtT2Val1, txtT2Val2, txtT2Result)
1 Botón (btnT2Calcular)
#Region "Calcular suma con
thread v2"
'Inicializo
un thread.
Private thT2 As System.Threading.Thread
Private Sub btnT2Calcular_Click(sender As Object, e As EventArgs) Handles btnT2Calcular.Click
thT2 = New System.Threading.Thread(AddressOf T2RealizarCalculo) With {.IsBackground = True}
thT2.Start()
End Sub
Private Sub T2RealizarCalculo()
Dim suma As Double = Val(txtT2Val1.Text) + Val(txtT2Val2.Text)
txtT2Result.Text = suma
End Sub
#End Region
La línea txtT2Result.Text
= suma, da el error “Operación no válida a través de subprocesos: Se tuvo
acceso al control”
Parte 4 – Suma con thread, sincronizando (v3)
Sincronizando threads.
Agregar:
3 TextBox (txtT3Val1, txtT3Val2, txtT3Result)
1 Botón (btnT3Calcular)
#Region "Calcular suma con
thread v3 Sincronizando threads"
'Contexto
de sincronización actual
Private T3syncContext As System.Threading.SynchronizationContext = System.Threading.SynchronizationContext.Current
'Inicializo
un thread.
Private thT3 As System.Threading.Thread
'Utilizo
una variable auxiliar donde se va a guardar el cálculo
Private T3Resultado As Decimal = 0D
Private Sub btnT3Calcular_Click(sender As Object, e As EventArgs) Handles btnT3Calcular.Click
thT3 = New System.Threading.Thread(AddressOf T3RealizarCalculo) With {.IsBackground = True}
thT3.Start()
End Sub
Private Sub T3RealizarCalculo()
T3Resultado = Val(txtT3Val1.Text) +
Val(txtT3Val2.Text)
'Threading.Thread.Sleep(1000)
'Al descomentar la linea anterior puede ver como el thread
queda detenido durante 1 segundo
'sin que
por ello se vea afectado el formulario principal que corre en otro thread. y
luego
'de ese
lapso se continúa la ejecución (resultado es puesto en el textbox
correspondiente)
'Cuando
el thread ya haya hecho lo suyo, ejecutamos un procedimiento
'sincronizado
el cual puede interactuar con el formulario sin problemas
T3syncContext.Send(AddressOf
T3ProcedimientoSincronizado, Nothing)
End Sub
''' <summary>Procedimiento
encargado de escribir el resultado en la caja de texto de resultado</summary>
Private Sub T3ProcedimientoSincronizado(s As Double)
txtT3Result.Text = T3Resultado
End Sub
#End Region
No hay comentarios:
Publicar un comentario