none
¿Como puedo ejecutar un Foreach por medio de un Task usando Async/Await? RRS feed

  • Pregunta

  • Hola

    Estoy trabajando con Visual Studio 2015, WPF, Kinect. Necesito ejecutar un Foreach en un Task, el problema que tengo es que  cuando me para frente al dispositivo Kinect el control Canvas que me sirve como lienzo la imagen se congela por varios segundos fácil son 20 segundos. Muestro el código que ocupo.

    private void KSensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
            {
                string manoDerecha = string.Empty;
                string manoIzquierda = string.Empty;
                Skeleton[] skeletons;
                using (var frame = e.OpenSkeletonFrame())
                {
                    if (frame == null) return;
                    skeletons = new Skeleton[frame.SkeletonArrayLength];
                    frame.CopySkeletonDataTo(skeletons);
                }
    
                int iSkeleton = 0;
                var brushes = new Brush[6];
                brushes[0] = new SolidColorBrush(Color.FromRgb(255, 0, 0));
                brushes[1] = new SolidColorBrush(Color.FromRgb(0, 255, 0));
                brushes[2] = new SolidColorBrush(Color.FromRgb(64, 255, 255));
                brushes[3] = new SolidColorBrush(Color.FromRgb(255, 255, 64));
                brushes[4] = new SolidColorBrush(Color.FromRgb(255, 64, 255));
                brushes[5] = new SolidColorBrush(Color.FromRgb(128, 128, 255));
    
                skeletonCanvas.Children.Clear();
                
                foreach (var data in skeletons)
                {
                    if (SkeletonTrackingState.Tracked == data.TrackingState)
                    {
                        Joint jointManoDerecha = data.Joints[JointType.HandRight];
                        Joint jointManoIzquierda = data.Joints[JointType.HandLeft];
    
                        SkeletonPoint posicionManoDerecha = jointManoDerecha.Position;
                        SkeletonPoint posicionManoIzquierda = jointManoIzquierda.Position;
    
                        manoDerecha = string.Format("Mano derecha: X:{0:0.0} Y:{1:0.0} Z{2:0.0}", posicionManoDerecha.X, posicionManoDerecha.Y,
                            posicionManoDerecha.Z);
    
                        manoIzquierda = string.Format("Mano izquierda: X:{0:0.0} Y:{1:0.0} Z{2:0.0}", posicionManoIzquierda.X, posicionManoIzquierda.Y,
                            posicionManoIzquierda.Z);
    
                        // Dibujar huesos
                        Brush brush = brushes[iSkeleton % brushes.Length];
                        skeletonCanvas.Children.Add(GetBodySegment(data.Joints, brush, JointType.HipCenter, JointType.Spine, JointType.ShoulderCenter, JointType.Head));
                        skeletonCanvas.Children.Add(GetBodySegment(data.Joints, brush, JointType.ShoulderCenter, JointType.ShoulderLeft, JointType.ElbowLeft, JointType.WristLeft, JointType.HandLeft));
                        skeletonCanvas.Children.Add(GetBodySegment(data.Joints, brush, JointType.ShoulderCenter, JointType.ShoulderRight, JointType.ElbowRight, JointType.WristRight, JointType.HandRight));
                        skeletonCanvas.Children.Add(GetBodySegment(data.Joints, brush, JointType.HipCenter, JointType.HipLeft, JointType.KneeLeft, JointType.AnkleLeft, JointType.FootLeft));
                        skeletonCanvas.Children.Add(GetBodySegment(data.Joints, brush, JointType.HipCenter, JointType.HipRight, JointType.KneeRight, JointType.AnkleRight, JointType.FootRight));
    
                        // Dibujar articulaciones
                        foreach (Joint joint in data.Joints)
                        {
                            Point jointPos = GetDisplayPosition(joint);
                            var jointLine = new Line();
                            jointLine.X1 = jointPos.X - 3;
                            jointLine.X2 = jointLine.X1 + 6;
                            jointLine.Y1 = jointLine.Y2 = jointPos.Y;
                            jointLine.Stroke = _jointColors[joint.JointType];
                            jointLine.StrokeThickness = 6;
                            skeletonCanvas.Children.Add(jointLine);
                        }
                    }
                    iSkeleton++;
                } // para cada esqueleto

    El primer Foreach es el que hace el trabajo pesado y para que no se congele la imagen que se ve en el control necesito liberarlo mediante un Task usando Async/Await.

    El problema que veo es que hay un control envuelto en el primer Foreach que esta dentro del segundo Foreach y por regla los controles tienen que estar en primer plano.

    Saludos!!!


    Pedro Ávila
    "El hombre sabio querrá estar siempre con quien sea mejor que él."
    Lima - Perú


    • Editado Pedro Ávila viernes, 10 de noviembre de 2017 0:24
    jueves, 9 de noviembre de 2017 23:23

Respuestas

  • hola

    el skeletonCanvas es el control que esta en la view ? porque si es asi veo que lo accedes en ambos foreach

    el acceder a controles implica pasar de un thread a otro con lo cual vas a tener el mismo efecto de freeze en la pantalla, quizas reduzca un poco, pero igual va a persistir

    se supone que cuando usas thread deberias procesar lo maximo posible sin acceder a controles y hacersto justo al final, quizas tener huesos y articulaciones listo y al final ponerlo en canvas

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    viernes, 10 de noviembre de 2017 14:15

Todas las respuestas

  • Buenas Pedro,

    Dado el tremendo volumen de datos que vas a procesar, no te seria mejor utilizar un thread (o más) para procesarlo?

    Digo esto porque si tienes una carga de trabajo pesada, con métodos de asincronismo va a ser difícil que el interfaz siga siendo fluida.

    Por el tema de acceder al control, no tienes porque preocuparte, ya que se puede acceder desde otros hilos mediante invocacion:

    Dispatcher.Invoke((Action)(() =>
    {
        canvas.Children.Clear();
        //Resto de tu codigo para el canvas
    }));

    Otra posibilidad es ejecutar el código mediante Parallel.ForEach(....), para aprobechar al maximo los recursos del sistema, y sacar los resultados mediante una invocacion asincrona desde el Dispatcher (No tengo el pc delante, asi que puede que haya algun fallo:

    Parallel.ForEach(skeletons, (data) =>
                    {
                        Dispatcher.BeginInvoke((Action)(() =>
                        {
                            if (SkeletonTrackingState.Tracked == data.TrackingState)
                            {
                                Joint jointManoDerecha = data.Joints[JointType.HandRight];
                                Joint jointManoIzquierda = data.Joints[JointType.HandLeft];
    
                                SkeletonPoint posicionManoDerecha = jointManoDerecha.Position;
                                SkeletonPoint posicionManoIzquierda = jointManoIzquierda.Position;
    
                                manoDerecha = string.Format("Mano derecha: X:{0:0.0} Y:{1:0.0} Z{2:0.0}", posicionManoDerecha.X, posicionManoDerecha.Y,
                                    posicionManoDerecha.Z);
    
                                manoIzquierda = string.Format("Mano izquierda: X:{0:0.0} Y:{1:0.0} Z{2:0.0}", posicionManoIzquierda.X, posicionManoIzquierda.Y,
                                    posicionManoIzquierda.Z);
    
                                // Dibujar huesos
                                Brush brush = brushes[iSkeleton % brushes.Length];
                                skeletonCanvas.Children.Add(GetBodySegment(data.Joints, brush, JointType.HipCenter, JointType.Spine, JointType.ShoulderCenter, JointType.Head));
                                skeletonCanvas.Children.Add(GetBodySegment(data.Joints, brush, JointType.ShoulderCenter, JointType.ShoulderLeft, JointType.ElbowLeft, JointType.WristLeft, JointType.HandLeft));
                                skeletonCanvas.Children.Add(GetBodySegment(data.Joints, brush, JointType.ShoulderCenter, JointType.ShoulderRight, JointType.ElbowRight, JointType.WristRight, JointType.HandRight));
                                skeletonCanvas.Children.Add(GetBodySegment(data.Joints, brush, JointType.HipCenter, JointType.HipLeft, JointType.KneeLeft, JointType.AnkleLeft, JointType.FootLeft));
                                skeletonCanvas.Children.Add(GetBodySegment(data.Joints, brush, JointType.HipCenter, JointType.HipRight, JointType.KneeRight, JointType.AnkleRight, JointType.FootRight));
    
                                // Dibujar articulaciones
                                foreach (Joint joint in data.Joints)
                                {
                                    Point jointPos = GetDisplayPosition(joint);
                                    var jointLine = new Line();
                                    jointLine.X1 = jointPos.X - 3;
                                    jointLine.X2 = jointLine.X1 + 6;
                                    jointLine.Y1 = jointLine.Y2 = jointPos.Y;
                                    jointLine.Stroke = _jointColors[joint.JointType];
                                    jointLine.StrokeThickness = 6;
                                    skeletonCanvas.Children.Add(jointLine);
                                }
                            }
                            Interlocked.Increment(iSkeleton);
                        };
                    });

    Tienes que añadir System.Threading.Task

    En teoria, este codigo reemplaza todo el bucle 

    foreach (var data in skeletons)
    {
    .........
    ......
    }

    Pruebalo y nos comentas, piensa tambien si quieres probar con threads y te hago un ejemplo

    Atte

     


    No olvides votar mi comentario si te ha ayudado y marcarlo como respuesta si ha sido la solución, con eso ayudas a mejorar mi reputación en la comunidad y a identificar la respuesta a la gente que tenga el mismo problema.

    Para obtener una respuesta lo más rápida y concisa posible, te recomiendo:



    viernes, 10 de noviembre de 2017 12:08
  • hola

    el skeletonCanvas es el control que esta en la view ? porque si es asi veo que lo accedes en ambos foreach

    el acceder a controles implica pasar de un thread a otro con lo cual vas a tener el mismo efecto de freeze en la pantalla, quizas reduzca un poco, pero igual va a persistir

    se supone que cuando usas thread deberias procesar lo maximo posible sin acceder a controles y hacersto justo al final, quizas tener huesos y articulaciones listo y al final ponerlo en canvas

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    viernes, 10 de noviembre de 2017 14:15
  • Hola @Leandro

    el skeletonCanvas es el control que esta en la view?

    Si.


    Pedro Ávila
    "El hombre sabio querrá estar siempre con quien sea mejor que él."
    Lima - Perú

    sábado, 11 de noviembre de 2017 16:28