hdolder.com srl  

       Software R&D
     hdc Home    |    Contenido    |    KO1    |    Director    |    Direcciones    |    email    |   Twitter   
  M&P TBW - Parallel XAML
                                        v188

M&P TBW se basa en el lenguaje XAML y su engine Dataflow.

Los BLOCKS M&P son UserControls XAML con algunas interfaces adicionales requeridas por el Runtime de M&P (MPR).

La UI Thread

Si bien XAML es fundamentalmente un "general purpose declarative language" muchos de sus elementos están orientados a la interoperación con los engines de visualización de WPF y Silverlight (SL). Estos engines de visualización operan en un Thread propio que se denomina "UI Thread" .

En el contexto de WPF/SL el Multi-threading es la excepción, no la regla, ya que todas las operaciones realizadas en Threads distintas de la UI Thread que interoperan con el engine de visualización deben ser "encoladas" (queued) para su ejecución en la UI Thread (ver WPF, Silverlight y el Multi-threading).

En el contexto de visualización los Controles XAML que componen la UI tienen "Properties" con una funcionalidad extendida comparadas con las "Properties" normales de .NET. Estas Properties extendidas se denominan "Dependency Properties" (DP). En esta documentación nos referimos a las Properties normales de .NET como "Common Properties" (CP).

En XAML todas las operaciones realizadas en Threads distintas de la UI Thread que manipulen DPs deben ser "encoladas" para su ejecución en la UI Thread.

EL Runtime de M&P es independiente de WPF y Silverlight y no introduce restricciones al Multithreading. Como veremos, en M&P el Multithreadind es la regla.

 M&P considera al engine de visualización de WPF/SL un dispositivo periférico de input-output (lo que en realidad es). Para M&P el UI Thread es "un Thread más" (es sólo el Thread que utiliza el engine de visualización).

Las DPs juegan un rol sumamente importante en el contexto de visualización pero imponen muy severas restricciones al Paralelismo, por lo que en los BLOCKS M&P se usan Common Properties (CPs).

El Binding de XAML permite conectar DPs con CPs pero con la restricción de que las "target" properties deben ser DPs. En M&P el BLOCK BBinding opera sin esta restricción y es, como veremos, una pieza clave en el Procesamiento Paralelo.

En la sección "Implicit Parallelism" mencionamos que M&P implementa el concepto de "paralelización implícita" con el objetivo de permitir que el Desarrollador se pueda desentender de los detalles del procesamiento paralelo para concentrarse en los detalles específicos de la aplicación.

El BLOCK Thread

En M&P cada BLOCK, por default, opera en un Thread propio que se denomina "BLKThread".

Parallel XAML

M&P obtiene un "Parallel XAML" extendiendo la funcionalidad de XAML incorporando tres elementos:

Queued Properties (QPs)

Son Common Properties (CPs) acopladas al "Concurrent Queue" contenido en los BLOCKS M&P. Mas abajo describimos su funcionalidad en detalle.

El BLOCK BBinding

Este es el BLOCK de Binding "universal" de M&P y es una pieza fundamental del procesamiento paralelo.

La Clase BUIThread

Permite "encolar" sincrónicamente (EnqueueAndWait) o asincrónicamente (Enqueue)  operaciones en la UI Thread. Es usada internamente en el BLOCK BBinding pero está disponible para ser usada en cualquier BLOCK.

En la sección "Implicit Parallelism" mencionamos también que los BLOCKS de M&P operan en paralelo por default pero que el grado de paralelismo puede ser limitado fácilmente por el desarrollador. Por ejemplo el BLOCK BBinding tiene una propiedad booleana "Sync" que permite indicar que el procesamiento debe ser sincrónico (el valor default de Sync es false).

Queued Properties (QPs)

Para facilitar la comprensión de las QPs consideremos un ejemplo en el que tres BLOCKS integran un simple "dataflow pipeline"

que debe procesar un conjunto de items I1, I2, ... IN.

Sin procesamiento paralelo cada item "captura" el pípeline completo durante su procesamiento y solo un item puede ser procesado a la vez. Pero supongamos que estamos operando en un procesador "quad-core". En este caso tendríamos tres procesadores libres en espera de trabajo. Esta la situación típica con aplicaciones puramente secuenciales.

Pero imaginemos ahora que mediante multithreading habilitemos el procesamiento paralelo. La situación ideal seria:

y luego:
 

Pero, que sucedería si por alguna razón se produce la situación siguiente ?:

En esta situación los ítems I3 e I4 estarían compitiendo por el procesamiento del BLOCK [B].

Esta condición competitiva se denomina "race condition" y debería resolverse mediante las técnicas habituales de multithreading: locking, syncronization, etc.

Para evitar la condición competitiva la solución en M&P consiste en establecer una "cola" (buffer)  en la entrada de cada BLOCK. De esta manera en la imagen anterior el item I4 estaría en la cola del BLOCK [B] hasta que este termine de procesar el item I3.

Si lográramos esconder la cola tendríamos una "paralelización implícita" perfecta.

Pero como no podemos modificar el compilador ni el runtime ,NET no podemos ocultar completamente la cola y los elementos asociados. Para minimizar la paralelización explicita introducimos en M&P las Queued Properties (QPs).

Como veremos al final, el esquema resultante es mas fácil de usar que de explicar.

Los elementos básicos del esquema son (en cada BLOCK):

  • El Catalogo de Handlers (HC).
  • El "Concurrent Queue" (CQ).
  • El "BLOCK Thread" (BLKThread).
  • El "Queued Property Processor" (BQPP).

Los tres primeros están ocultos y son manejados por el BQPP.

El Catalogo de Handlers (HC) es un diccionario que contiene los delegates de los métodos que deben invocarse para procesar los valores que reciben las diferentes QPs. La clave del diccionario es el nombre de la QP.

El "Concurrent Queue" (CQ) es una cola FIFO (queue) thread-safe que puede ser cargada desde múltiples Threads y también ser descargada desde múltiples Threads. El CQ implementa el conocido pattern "Producer-Consumer" y el único Consumer es el BLKThread.

El "BLACK Thread" (BLKThread) es un Thread utilizado por el BQPP para invocar (despachar) los delegates asociados con las diferentes QPs.

El "Queued Property Processor" (BQPP) maneja la operación del conjunto.

Internamente el BQPP al encontrar un valor en el tope del CQ lo extrae y "despacha" el handler registrado en el HC (por el Desarrollador) pasándole el valor. La ejecución del handler es sincrónica.

Todos los elementos del esquema se instancian automáticamente al instanciarse el BLOCK que los contiene.

Cada BLOCK tiene una referencia al objeto BQPP en una Property predefinida denominada QPP que el desarrollador debe usar en la lógica del BLOCK.

La interface de BQQP disponible para el Desarrollador es simple y consta de solo tres métodos:

interface IBQPP
{
void Enqueue(string propname, object value);
void RegPCH(string propname, Action<object> propdel);
void StartDispatching();
}

A continuación incluimos como ejemplo el código de un custom BLOCK que implementa QPs y de un programa que interactúa con el custom BLOCK.

El código contiene comentarios detallados. En MainWindow.xaml puede verse la utilización del BLOCK BBinding.

ParallelBLOCK.cs

MainWindow.xaml

MainWindow.xaml.cs


 

El siguiente es un fragmento del código de ParallelBLOCK.cs:

    public class ParallelBLOCK : BLOCK
    {
        public ParallelBLOCK()
        {
            // Registracion de delegates
            QPP.RegPCH("PropI", PropIPCH);
            QPP.RegPCH("PropS", PropSPCH);
        }

        public override void BStart()
        {
            if (BIsStarted)
                return;
            BIsStarted = true;

            // Arranca el Dispatching
            QPP.StartDispatching();
        }

        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        // Input Queued Property
        private int _PropI = -1;  // <<<<<<<<<<< 
        public int PropI
        {
            set
            {
                // Encola el valor
                QPP.Enqueue("PropI", value);
            }
        }

        // Handler de la Property PropI
        void PropIPCH(object value)
        {
            OutS = "@" + value;
        }

        // Input Queued Property
        private String _PropS = "";  // <<<<<<<<<<< 
        public String PropS
        {
            set
            {
                // Encola el valor
                QPP.Enqueue("PropS", value);
            }
        }

        // Handler de la Property PropS
        void PropSPCH(object value)
        {
            OutS = "" + value;
        }

        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        // Output Property del BLOCK
        private String _OutS = "";
        public String OutS
        {
            get
            {
                return _OutS;
            }
            set
            {
                if (_OutS == value)
                    return;

                _OutS = value;

                OnPropertyChanged("OutS");
            }
        }
    }

Ver también

Links

 

  TBW The BLOCKS World

©2012 hdolder.com srl  

Cp3jZgwcH
2011-12-19