Que tengan un buen dia a todos los integrantes del foro.
Quiero hacerles la consulta, cuando utilizo thread dentro de otro thread se bloquea mi ventana, no se si es bueno aplicar un hilo dentro de otro hilo, si trabajo con un hilo todo bien pero agrego otro hilo dentro de la misma es ahi donde se bloque mi ventana.
Tal ves no tenga sentido pero lo que quiero hacer es cargar de un datatable a listview pero con el primer hilo tarda en cargar datos en el listview y por cada fila al agregar un item en listview se tarda pero la idea es acelerar la carga y la idea que tuve es por cada fila en datatable disparar otro hilo y es ahí donde empieza a bloquear mi ventana, pero la carga de datos a listview es rápido.
la pregunta es si es conveniente utilizar un hilo dentro de otro o hay otra forma de hacer hilos anidados si que me bloquee mi ventana.
El código es lo siguiente para que se me entienda bien...
Gracias de antemano por sus respuestas
Imports System.Data.SqlClient
Imports System.Threading
Imports System.ComponentModel
<Microsoft.VisualBasic.ComClass()> <ToolboxBitmap(GetType(System.Windows.Forms.ListView))> <System.Serializable()> Public Class ListView
Inherits Windows.Forms.ListView
Dim thSQL As Thread
Dim _sq As String, _decimales As Integer, _EllipseIcon As Boolean, _bordColor As Pen
Public Sub SQLConsulta(ByVal sq As String, Optional ByVal EllipseIcon As Boolean = False, Optional ByVal borderColor As Pen = Nothing, Optional ByVal decimales As Integer = 0)
_sq = sq : _decimales = decimales : _EllipseIcon = EllipseIcon : _bordColor = borderColor
thSQL = New Thread(AddressOf SQLConsultaInterno)
thSQL.Start()
End Sub
Private Sub SQLConsultaInterno()
Try
Dim h As Thread
Dim conexion As SqlConnection = New SqlConnection()
conexion.ConnectionString = "Data Source=ip_server,1433;Failover Partner=ip_server2,1433;Network Library=DBMSSOCN;MultipleActiveResultSets=true;Initial Catalog=DB;User ID=sa;Password=contraseña;" : conexion.Open()
Dim ds As New DataSet(), da As New SqlDataAdapter(_sq, conexion) : da.Fill(ds, "tabla")
If ds.Tables("tabla").Columns.Count = 1 Then
For c As Integer = 0 To ds.Tables("tabla").Columns.Count - 1
AddColumn(ds.Tables("tabla").Columns(c).Caption)
Next
Else
For c As Integer = 1 To ds.Tables("tabla").Columns.Count - 1
AddColumn(ds.Tables("tabla").Columns(c).Caption)
Next
End If
With ds.Tables("tabla")
For filas As Integer = 0 To .Rows.Count - 1
Dim ob As New ROW
ob.fila = .Rows(filas)
ob.columnas = .Columns.Count
ob.index = filas
h = New Thread(AddressOf agregar)
h.Start(ob)
Next
End With
conexion.Close()
Catch ex As Exception
End Try
End Sub
Private Sub agregar(obj As Object)
Dim rw As ROW = DirectCast(obj, ROW)
If rw.columnas = 1 Then
Dim Registro As New ListViewItem(rw.fila.Item(0).ToString.Trim, rw.index)
AddItem(Registro)
Else
Dim Registro As New ListViewItem(rw.fila.Item(1).ToString.Trim, rw.index)
Registro.Tag = rw.fila.Item(0).ToString.Trim
For c As Integer = 2 To rw.columnas - 1
Registro.SubItems.Add(rw.fila.Item(c).ToString().Trim)
Next
AddItem(Registro)
End If
End Sub
Delegate Sub AddColumns(ByVal texto As String)
Private Sub AddColumn(ByVal texto As String)
If Me.InvokeRequired Then
Dim d As New AddColumns(AddressOf AddColumn)
Invoke(d, New Object() {texto})
Else
Me.Columns.Add(texto)
End If
End Sub
Delegate Sub AddItems(ByVal item As ListViewItem)
Private Sub AddItem(ByVal item As ListViewItem)
If Me.InvokeRequired Then
Dim d As New AddItems(AddressOf AddItem)
Invoke(d, New Object() {item})
Else
Me.Items.Add(item)
End If
End Sub
Private Class ROW
Property fila As DataRow
Property columnas As Integer
Property index As Integer
End Class
End Class
De entrada el tema, no debiera haberse puesto aquí. este hilo es para VB6.0 y versiones previas, el adecuado es en .net: https://foro.elhacker.net/net-b62.0/
El tema d ela actualización de vistas, ya se ha tratado.
Se ralentiza cuando un control tras añadir un ítem, trata de actualizar la vsta... entonces si en vez de querer añadir 1 sólo ítem se pretende añadir cientos o miles, el control debe repintarse cientos o miles de veces, de ahí la lentitud...
La solución?, Hay varias...
1 - La más sencilla es ver si el control admite un método AddRange(items), con lo que puedes pasarle un array o colección de algún tipo... (ver sobrecargas).
2 - Otra opción en NET (cuando programas controles del propio NET) es usar los métodos:
objeto.BeginUpdate
objeto.DataSource = elArray
objeto.EndUpdate
...cuya misión es precisamente evitar el redibujado. BeginUpdate y EndUpdate marcan el inicio y fin del 'NO-redibujado'...
3 - La práctica cuando el objeto no admite ninguna opción al respecto:
objeto.visible =false
bucle desde 0 hasta array.count ' o el tope del objeto que sea.
objeto.addItem(array(k))
fin bucle
objeto.visible=true
De hecho este mismo código puede ser parte de una función que tú crees llamada precisamente AddRange(coleccion de algún tipo)
De este modo, fuerzas a que no se redibuje con cada añadido. El propio control (salvo que esté muy mal programado), cuando se añade un ítem no intentará redibujar si resulta que el control no está visible, en consecuencia no pierde tiempo en redibujarlo x veces, si no sólo una al final, cuando lo haces visible de nuevo.
Entonces te darás cuenta que no necesitas múltiples hilos para actualizar un control, de hecho no es aconsejable, porque se pelearán (competirán) por el acceso al control. Los hilos múltiples son adecuados para trareas asíncronas, y esta es una tarea síncrona.
P.d.:
OJO: El pseudocódigo:
objeto.visible =false
bucle desde 0 hasta array.count ' o el tope del objeto que sea.
objeto.addItem(array(k))
fin bucle
objeto.visible=true
No considera el caso de que el objeto esté invisible, en realidad si fue ocultado desde otro lado, aquí no debería hacerse visible.
Buleano v = objeto.visible
objeto.visible =false
bucle desde 0 hasta array.count ' o el tope del objeto que sea.
objeto.addItem(array(k))
fin bucle
objeto.visible=v ' esto es, reasignamos el mismo estado que tenía previamente
La solución óptima ya te la ha explicado el compañero @NEBIRE, así que me centraré en puntualizar algunas otras cosas mejorables y que son bastante importantes:
1. La próxima vez, y siempre que las circunstancias sean las correctas, en lugar de optar por el uso de la clase System.Threading.Thread deberías utilizar la clase System.ComponentModel.BackgroundWorker, ya que está especificamente diseñada para simplificar y optimizar la interacción con la UI mediante su modelo de clase orientado a eventos.
Para todo lo demás, en lugar de la clase System.Threading.Thread deberías recurrir a la clase System.Threading.Tasks.task, o hacer uso de las palabras reservadas Async y Await. En general utilizar la clase Thread es una solución poco óptima si no existe una necesidad real de crear un Thread de forma explícita, como sería por ejemplo para crear y configurar opciones por thread (sería algo similar a configurar opciones por aplicación u opciones por usuario), o para crear un Thread persistente (durante el tiempo de ejecución de la aplicación) que necesite ser identificado cada cierto tiempo por el motivo que fuese...
2. Siempre que una clase no tenga la necesidad de ser heredable, como es el caso de la clase que definiste en tu código con nombre "Row", deberías asignarle el modificador NotInheritable a la firma del miembro para obtener una micro-optimización en la instanciación del tipo y por ende una micro-optimización más notable en la carga de datos de varios elementos del mismo tipo al representarlos en un control.
3. En tu clase o control de usuario "ListView" estás utilizando tipos que implementan la interfáz IDisposable como es el caso de la clase System.Drawing.Pen, al instanciar este tipo de clases generan recursos no administrados (nativos) que deben ser liberados mediante el método IDisposable.Dispose() ( Pen.Dispose() ), así que deberías sustituir la implementación heredada de la interfáz IDisposable para completar su funcionalidad liberando esos nuevos recursos no heredados que estás utilizando en tu código...
En este caso en específico con objetos de tipo Pen y SolidBrush y etc. se debe tener en cuenta que existe un límite máximo de recursos de GDI que un proceso puede mantener abiertos (el cual está especificado en el registro de Windows, como muchas otros límites varios), aunque dicho límite es exageradamente elevado y eso lo hace casi "inalcanzable" por una aplicación de consumo general... claro está a menos que los recursos se administren mal, si generamos varias decenas de miles de recursos nativos GDI en un intervalo corto de tiempo, un intervalo más corto del que el GC puede recojer más recursos a tiempo de los que se generan, y rebasar dicho límite.
Lo que quiero decir es que... el método Dispose está para algo, existe para ser utilizado, para ayudar al GC en la liberación de dichos recursos en el intervalo más corto posible de tiempo, no para ignorarlo y dejar que el GC empiece la liberación cuando le de la gana (o que al final no lo haga), así que dale uso al método Dispose, este mensaje va dirigido a todo el mundo/programador de .NET que lo lea, que estoy bastante mosqueado de ver siempre como personas "aprenden a programar en .NET" sin aprender lo básico y fundamental para elaborar un programa estable y sin fugas, pues no liberar los recursos de un objeto cuando ya no se necesitan ser utilizados es algo que se considera una de las peores y más horribles prácticas en la programación orientada a objetos.
Ejemplo:
Public Class MyListView : Inherits ListView
Private MyPen As Pen
Public Sub New()
Me.MyPen = New Pen(Color.Empty)
End Sub
Protected Overrides Sub Dispose(disposing As Boolean)
If (disposing) Then
' Aquí llamas al método Pen.Dispose()
' y adicionálmente anulas la referencia
' para optimizar (acortar) el intervalo de tiempo
' en el que efectuará el GarbageCollector.
Me.MyPen.Dispose()
Me.MyPen = Nothing
End If
' Aquí llamas al método base Dispose() de la herencia para liberar los
' recursos por defecto utilizados por la clase ListView.
MyBase.Dispose(disposing)
End Sub
End Class
Saludos.
Bueno agradecerles a nebire y elektro por sus conceptos bien puntualizados, sobre todo en la administración de recursos de un objeto.
Les agradezco mucho por haberme despejado dudas sobre programación en net.