[ASP.NET] Cargando datos en paralelo con Parallel.Invoke

Hola, hoy quiero mostrarles una forma en la cual podemos realizar tareas en paralelo de una manera muy sencilla, aclaro que no es la única forma, sin embargo me gusta bastante usar Parallel.Invoke por su facilidad de uso, sin embargo debemos tener algunos puntos pendientes:

  • Los métodos que se llaman no pueden recibir parámetros
  • No se garantiza un orden específico en la ejecución de los métodos

Pueden consultar la documentación oficial en MSDN: Parallel.Invoke

Pero veamos un ejemplo, suponemos que tenemos una página en la cual queremos mostrar en un control gridview las personas y en otro grid los productos de una base de datos, si lo miramos tradicionalmente primero se ejecutara un método y después el otro, es decir de manera secuencial, en ese caso vamos a declarar dos métodos que van a cargar los datos en los gridview:

   1:  private void CargarPersonas()
   2:  {
   3:      grvPersonas.DataSource = BDManager.getInstance.ObtenerPersonas();
   4:      grvPersonas.DataBind();
   5:  }
   6:   
   7:  private void CargarProductos()
   8:  {
   9:      grvProductos.DataSource = BDManager.getInstance.ObtenerProductos();
  10:      grvProductos.DataBind();
  11:  }

En ambos métodos, se hace un llamado a la clase BDManager la cual tiene los métodos para conectarnos a la base de datos, el código de dicha clase es:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:   
   5:  namespace ParallelInvoke
   6:  {
   7:      public class BDManager
   8:      {
   9:          private static volatile BDManager _uniqueInstance;
  10:          private static readonly Object syncRoot = new Object();
  11:   
  12:          private BDManager() { }
  13:   
  14:          public static BDManager getInstance
  15:          {
  16:              get
  17:              {
  18:                  if (_uniqueInstance == null)
  19:                  {
  20:                      lock (syncRoot)
  21:                      {
  22:                          if (_uniqueInstance == null)
  23:                              _uniqueInstance = new BDManager();
  24:                      }
  25:                  }
  26:                  return _uniqueInstance;
  27:              }
  28:          }
  29:   
  30:          public IEnumerable<Person> ObtenerPersonas()
  31:          {
  32:              using (var context = new AdventureWorks2008R2Entities())
  33:              {
  34:                  return context.People.ToList();
  35:              }
  36:          }
  37:   
  38:          public IEnumerable<Product> ObtenerProductos()
  39:          {
  40:              using (var context = new AdventureWorks2008R2Entities())
  41:              {
  42:                  return context.Products.ToList();
  43:              }
  44:          }
  45:      }
  46:  }

Lo único raro de la clase es que estamos usando un Singleton para crear solo una única instancia de la clase, en el siguiente post lo veremos más en detalle, pero volviendo al tema principal, para cargar los datos de la manera tradicional lo que podemos tener es:

   1:  private void CargarDatos1()
   2:  {           
   3:      CargarPersonas();
   4:      CargarProductos();
   5:  }

Y como comente antes se ejecutará unos tras el otro, ahora si quisiéramos usar Parallel.Invoke sería:

   1:  private void CargarDatos2()
   2:  {
   3:      Parallel.Invoke(new Action[]{CargarPersonas, CargarProductos});
   4:  }

Como podemos observar el cambio es bastante sencillo, y finalmente si usamos el IntelliTrace de Visual Studio nos daremos cuenta que el hilo de cada método es diferente:

Imagen1

Espero les sea de utilidad y de interés este post, saludos!

You may also like...

18 Responses

  1. nicolocodev says:

    Hola.

    Es buen post Julito. Me quedan dos inquietudes, porque haces uso de la palabra clave VOLATILE? de que optimizacion te quieres librar en este caso?.. si es un singleton no habria problema con lo del Multitrheading, dado que si en algun momento el valor de la instancia cambia o vuelve a ser Null se vuelve a crear esta instancia y todos felices, no?

    Dado que los metodos se ejecutan en hilos distintos al principal… que mecanismo recomiendas en ASP.NET Para sincronizarlos con la UI nuevamente y no tener el problema de esa sabrosa excepcion que todos conocemos a la hora de trabajar con Multihilo.

    Saludos.

    • Gracias Nicolas, la palabra volatile para asegurar que todos los hilos accedan a la misma variable, ya q es posible q en un entorno multihilo se creen varias instancias de la clase asi se use singleton, entonces con volatile aseguro q todos los hilos usen la misma (eso en palabras sencillas), ademas la idea del singleton es tener 1 unica instancia siempre, sea multihilo o no.

      Pära sincrnizarlos, bueno eso es todo un tema, la idea del post era mostrar como ejecutar metodos multihilos, y o importante de parallel.invoke es q los metodos no son dependientes uno del otro.

      • nicolocodev says:

        Hola Julito, gracias por responder… Pero hasta donde se, realmente a lo que ayudaria volatile seria Cuando un hilo esta trabajando el objeto y quiere ser modificado por otro, Y/o siempre querer tener el ultimo valor en todo momento para todos los hilos, entonces no deberia ir esta en la propiedad publica? y así evitarse el lock?

        • La idea de usar volatile es q si un hilo esta usando el recurso y llega otro q lo necesita debe esperar a q el hilo anterior lo libere, y no Nico, no debe ir en la propiedad, ya que las propiedades no pueden tener este modificador…

          Gracias por tus comentarios… seria bueno q tratemnos temas de este tipo mas seguido, saludos!

    • Excelente tema, más aún cuando vemos el uso del patrón Singleton en acción en un ejemplo de la vida real, Nicolás tiene razón al ser Singleton ya nos estamos asegurando en parte el Thread Safe sin embargo, la implementación del Singleton tal cual como la vemos en el ejemplo es una implementación generalizada especificamente la del Java, aqui contamos con la rica plaraforma .NET en donde podemos optimizar el patrón para la plataforma, por ejemplo la propiedad estática static _uniqueInstance la podemo inicializar directamente el su delcaración ya que en .net los campos estaticos son los primeros en inicializarse además en .NET static nos garantiza que ya es Thread Safe por lo que no hay necesidad de un Object para hacer un lock que como mencionaba para mi es la trarucción literal del sync en Java. Entonces quedaría:

      // Inicializar campos static desde su declaración los hace Thread Safe en .NET no necesitamos hacer lock.
      private static BDManager _uniqueInstance = new BDManager();

      public static Instance
      {
      get
      {
      return _uniqueInstance;
      }
      }

      Ya no necesitamos volatile tampoco.

      • Gracias Jhonnys, es muy verdad lo que dices, esa implementacion del patron es mas de Java, y claro se podria hacer inicializando la variable, sin embargo me gusta mas como la coloque por dos razones:
        1. Me parece que se mas bonito :D
        2. En el constructor es posible realizar algunas otras acciones si es necesario claro.
        Saludos!

        • Claro la forma tal cual como la tienes es mucho mas sencilla de comprender por el motivo que verlo de esa manera se reconoce inmediatamente el patrón Singleton y más aún se tiene mucho más claro el porque del uso de lock para que sea Thread Safe, la optimización de .NET si no conocemos cuales son las funcionalidadese y ventajas de usar un static e inicializarlo de una pues no entendermos bien porque es Thread Safe, en fin Julio me parce un excelente ejemplo gracias por compartirlo.

      • nicolocodev says:

        Vale, si me parcecio desde el principio innecesario el uso de volatile en este ejemplo .NET ya es en si bastante seguro con este tema, lo que si NO me consta es el hecho de las violaciones de modificacion entre distintos hilos (y pese a que estos son “transversales”), tambien entre AppDomains, hice ya varios ejemplos y sin problemas, .net se comporta muy bien con el acceso a sus campos en esta implementación.

        …Me quedas debiendo lo referente al SynchronizationContext con ASP.NET :-)

        Saludos.

  2. nathan says:

    Reblogged this on Nathan.

  3. Little Shawn says:

    Hoa que tal, mi duda es, por qué los métodos que se llaman no deben recibir parámetros?

    • Hola Little Shawn, los métodos no pueden recibir parámetros pq en realidad lo que haces cuando utilizas parallel.invoke lo q estas haciendo es utilizar delegados y esos delegados tienen dos limitaciones que son: 1. no recibe parámetros y 2. no retorna un valor.

      Saludos!

      • mlizbeth says:

        Osea que si yo hago esto, está mal?

        Parallel.Invoke(() => oDatos.procesar_host(host[0]), () => oDatos.procesar_host(host[1]));

        • Hola, una muy buena pregunta y la respuesta es no,no está mal, todo lo contrario, es muy bueno hacerlo así, pero entonces surge la duda del pq allí si podemos usar argumentos, y la respuesta es pq no es lo mismo que si usarás Action, al usar la expresión lambda que es un método anónimo cada método anónimo genera el mismo método que al usar el Action (no recibe parámetros) pero internamente ese método anónimo llama a otro método que si acepta parámetros.

          Así que úsalo sin problema :D

          Saludos!

  4. Interesante Info, gracias por compartirla

  1. July 19, 2012

    [...] [ASP.NET] Cargando datos en paralelo con Parallel.Invoke [...]

  2. December 19, 2012

    [...] [ASP.NET] Cargando datos en paralelo con Parallel.Invoke [...]

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Follow

Get every new post on this blog delivered to your Inbox.

Join other followers: