[Tips]Ejecutar métodos en paralelo en ASP.NET 3.5
Hola, en esta ocasión quiero volver a tocar el tema de la ejecución en paralelo de métodos en ASP.NET, que si bien está muy optimizado en la versión 4.5, que aún está en desarrollo, muchos de nosotros no tenemos planes a corto plazo de migrar para aprovechar las ventajas.
Veamos un escenario común, grillas grillas combos combos, a que me refiero con esto, es que muchas veces nuestros sistemas cargan una cantidad considerable de combos que no están asociados e igual cantidad de grillas en el Page Load. Generalmente no es necesario que esto se cargue secuencialmente, y podemos aprovechar las ventajas en términos de tiempo al utilizar la carga en paralelo.
Para explicar un poco más vamos a crear un pequeño proyecto, que tenga 2 DropDownList (o combos, como le llamamos universalmente). Cada uno de estos combos va a tener una duración de carga de 3 segundos, obviamente de manera secuencial se va a demorar mínimo 6 segundos la carga de la página, mientras que en paralelo, claro como el agua, mínimo 3 segundos:
Ahora veamos el código
El código de la página con carga secuencial:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
dd_ciudades.DataSource = Obtciudades()
dd_ciudades.DataBind()
dd_regiones.DataSource = Obtregiones()
dd_regiones.DataBind()
End If
End Sub
Public Function Obtciudades() As List(Of Ciudad)
System.Threading.Thread.Sleep(3000)
Dim ciudades As New List(Of Ciudad)
ciudades.Add(New Ciudad With {.id = 1, .nombre = "Concepcion"})
ciudades.Add(New Ciudad With {.id = 2, .nombre = "Santiago"})
ciudades.Add(New Ciudad With {.id = 3, .nombre = "Viña del Mar"})
ciudades.Add(New Ciudad With {.id = 4, .nombre = "Puerto Montt"})
ciudades.Add(New Ciudad With {.id = 5, .nombre = "Cañete"})
Return ciudades
End Function
Public Function Obtregiones() As List(Of Region)
System.Threading.Thread.Sleep(3000)
Dim regiones As New List(Of Region)
regiones.Add(New Region With {.id = 1, .nombre = "Primera Region"})
regiones.Add(New Region With {.id = 2, .nombre = "Segunda Region"})
regiones.Add(New Region With {.id = 3, .nombre = "Tercera Region"})
regiones.Add(New Region With {.id = 4, .nombre = "Cuarta Region"})
regiones.Add(New Region With {.id = 5, .nombre = "Quinta Region"})
Return regiones
End Function
Puedes notar los métodos Sleep para causar la demora en el thread de ejecución en cada uno de los métodos que sirvan para poblar los dropdownlist.Ahora si vemos la carga del la página con el IE Developer Toolbar, el resultado son 6.07 segundos:

Veamos el código para ASP.NET 3.5, es más largo pero la relación costo beneficio es muy buena, ya que vamos a bajar los tiempos casi a la mitad. Como dije anteriormente, implementar esta misma funcionalidad en ASP.NET 4.5 es mucho más sencillo, ya que elimina las complejidades al momento de especificar cuales métodos serán ejecutados de manera Asíncrona, pero eso lo dejaremos para otro post 
Imports Microsoft.VisualBasic
Imports System.Threading
Partial Class Paralelo
Inherits System.Web.UI.Page
Private ehRecuperarDatosDeDropDownList1 As EventHandler = Nothing
Private ehRecuperarDatosDeDropDownList2 As EventHandler = Nothing
Private listaRegiones As New List(Of Region)
Private listaCiudades As New List(Of Ciudad)
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim pat1 As New PageAsyncTask(New BeginEventHandler(AddressOf
Me.RecuperarDatosDeDropDownList1), New EndEventHandler(AddressOf
Me.CargarDropDownList1), Nothing, Nothing, True)
Dim pat2 As New PageAsyncTask(New BeginEventHandler(AddressOf
Me.RecuperarDatosDeDropDownList2), New EndEventHandler(AddressOf
Me.CargarDropDownList2), Nothing, Nothing, True)
Page.RegisterAsyncTask(pat1)
Page.RegisterAsyncTask(pat2)
End Sub
#Region "dd_regiones"
Private Function RecuperarDatosDeDropDownList1(sender As Object, e As
EventArgs, cb As AsyncCallback, state As Object) As IAsyncResult
Me.ehRecuperarDatosDeDropDownList1 = New EventHandler(AddressOf
Me.RecuperarRegiones)
Return Me.ehRecuperarDatosDeDropDownList1.BeginInvoke(Me,
EventArgs.Empty, cb, Nothing)
End Function
Private Sub RecuperarRegiones(sender As Object, e As EventArgs)
Me.listaRegiones = Obtregiones()
End Sub
Private Sub CargarDropDownList1(ar As IAsyncResult)
Me.ehRecuperarDatosDeDropDownList1.EndInvoke(ar)
Me.dd_regiones.DataSource = Me.listaRegiones
Me.dd_regiones.DataBind()
End Sub
Public Function Obtregiones() As List(Of Region)
System.Threading.Thread.Sleep(3000)
Dim regiones As New List(Of Region)
regiones.Add(New Region With {.id = 1, .nombre = "Primera Region"})
regiones.Add(New Region With {.id = 2, .nombre = "Segunda Region"})
regiones.Add(New Region With {.id = 3, .nombre = "Tercera Region"})
regiones.Add(New Region With {.id = 4, .nombre = "Cuarta Region"})
regiones.Add(New Region With {.id = 5, .nombre = "Quinta Region"})
Return regiones
End Function
#End Region
#Region "dd_ciudades"
Private Function RecuperarDatosDeDropDownList2(sender As Object, e As EventArgs, cb As AsyncCallback, state As Object) As IAsyncResult
Me.ehRecuperarDatosDeDropDownList2 = New EventHandler(AddressOf Me.RecuperarCiudades)
Return Me.ehRecuperarDatosDeDropDownList2.BeginInvoke(Me,
EventArgs.Empty, cb, Nothing)
End Function
Private Sub RecuperarCiudades(sender As Object, e As EventArgs)
Me.listaCiudades = Obtciudades()
End Sub
Private Sub CargarDropDownList2(ar As IAsyncResult)
Me.ehRecuperarDatosDeDropDownList2.EndInvoke(ar)
Me.dd_ciudades.DataSource = Me.listaCiudades
Me.dd_ciudades.DataBind()
End Sub
Public Function Obtciudades() As List(Of Ciudad)
System.Threading.Thread.Sleep(3000)
Dim ciudades As New List(Of Ciudad)
ciudades.Add(New Ciudad With {.id = 1, .nombre = "Concepcion"})
ciudades.Add(New Ciudad With {.id = 2, .nombre = "Santiago"})
ciudades.Add(New Ciudad With {.id = 3, .nombre = "Viña del Mar"})
ciudades.Add(New Ciudad With {.id = 4, .nombre = "Puerto Montt"})
ciudades.Add(New Ciudad With {.id = 5, .nombre = "Cañete"})
Return ciudades
End Function
#End Region
End Class
Ciertamente el código puede resultar confuso,(adjunto el código fuente, ya que se ve mejor en Visual Studio que en el blog), primero se registran las “tareas” que vamos a correr en paralelo, cada tarea tiene un método de inicio(BeginEventHandler), y método de finalización (EndEventHandler), luego registramos cada una de estas tareas, en este caso son dos, lo hacemos llamando al método RegisterAsyncTask de la página.
Los métodos ObtCiudades y ObtieneRegiones no sufren alteraciones, ahora al momento de ejecutar la página, obtenemos mejores tiempo, tal cual habíamos estimado, 3,16 segundos.

Claro está que solo estoy usando esta funcionalidad con dos métodos, puedes utilizarlos con más según tu conveniencia.
Puedes bajar el código desde acá

Espero que te sirva!
@chalalo