none
Problema con Multithreading RRS feed

  • Pregunta

  • ¡Buenas a todos! Esta va a ser una pregunta larga.

    Estoy experimentando algo un poco extraño con un programa Windows Form en el cual estoy aplicando técnicas de multithreading. La situación es la siguiente:

    Mi programa comienza creando una lista de objetos de tipo MyClass. MyClass tiene cuatro propiedades llamadas Level1, Level2, Level3 y Level4 que definen una jerarquía. Por ejemplo, estos dos objetos representan dos municipios de la Provincia de Madrid:

    {Level1 = "Europa", Level2 = "España", Level3="Madrid", Level4="Aranjuez"}

    {Level1 = "Europa", Level2 = "España", Level3="Madrid", Level4="Madrid"}

    Tengo un formulario Form1 que contiene un control TreeView llamado treeview1. En el Form_Load de Form1 obtengo una lista de objetos de esta clase en un List<MyClass> y a continuación genero un nuevo thread para ejecutar una función FillTree que se encarga de construir un árbol con los objetos de la lista. Hasta este punto todo bien, tengo dos threads: el background thread que está llenando el árbol y el user thread que presenta al usuario el formulario Form1.

    Para poder manipular el control de forma asíncrona he creado un par de funciones llamadas AddNode, polimórficas, una de las cuales recibe un TreeNode y lo inserta en la raíz del treeView y otra recibe dos nodos, padre e hijo, e inserta el hijo al padre. He creado sendos delegados llamados AddNodeDelegate y AddNodeDelegate1 para realizar la invocación de las funciones AddNode en el thread que controla el formulario.

    La función FillTree hace lo siguiente: de la lista de objetos MyClass obtiene mediante linq una lista de los distintos valores de Level1. Para cada valor de Level1, crea un treenode y llama a AddNode para agregar ese treenode en la raíz del treeview. A continuación, crea un nuevo thread que se va a encargar de agregar hijos en esa rama llamando a una función FillLevel2, que a su vez llama a una función FillLevel3 y a una función FillLevel4. En realidad estas no son relevantes porque ninguna de ellas genera nuevos threads.

    Lo que está ocurriendo al ejecutar el código es que al primer nodo el treeview le está agregando los hijos que corresponderían al segundo; al segundo los hijos que corresponderían al tercero y así sucesivamente.

    La pregunta es: ¿Estoy haciendo algo mal o es un bug de .Net? Yo tengo una hipótesis para explicar por qué está ocurriendo eso, pero quisiera confirmar si estoy cometiendo algún error en la manipulación de los threads; y también comprobar si les ocurre a otros desarrolladores (les agradecería mucho si prueban mi código y me confirman que ocurre lo que he descrito).

    He aquí el código:

    MyClass.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    
    namespace TreeViewMulti
    {
        class MyClass
        {
            public string Level1 { get; set; }
            public string Level2 { get; set; }
            public string Level3 { get; set; }
            public string Level4 { get; set; }
    
            public static List<MyClass> GetPlainList()
            {
                List<MyClass> list = new List<MyClass>();
    
                int[] i = new int[4];
    
                i[0] = 0;
                foreach (string d1 in Directory.GetDirectories("C:\\"))
                {                
                    try
                    {
                        i[1] = 0;
                        foreach (string d2 in Directory.GetDirectories(d1))
                        {                        
                            try
                            {
                                i[2] = 0;
                                foreach (string d3 in Directory.GetDirectories(d2))
                                {
                                    
                                    try
                                    {
                                        i[3] = 0;
                                        foreach (FileInfo f in new DirectoryInfo(d3).GetFiles(@"*.txt"))
                                        {
                                            list.Add(new MyClass
                                            {
                                                Level1 = string.Format("{0}: {1}",  i[0], d1),
                                                Level2 = string.Format("{0}/{1}: {2}", i[0],i[1], d2),
                                                Level3 = string.Format("{0}/{1}/{2}: {3}", i[0],i[1],i[2], d2),
                                                Level4 = string.Format("{0}/{1}/{2}/{3}: {4}", i[0], i[1], i[2], i[3], f.Name)
                                            });
                                            i[3]++;
                                        }
                                        i[2]++;
                                    }
                                    catch { }
                                }
                                i[1]++;
    
                            }
                            catch { }
    
                        }
                        i[0]++;
                    }
                    catch { }
    
                }
                return list;
            }
        }
    }
    Form1.cs
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    
    namespace TreeViewMulti
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
    
    
    
            // Esta función agrega un nodo al Treeview
            // Usamos this.Invoke para asegurar que el control TreeView sólo es modificado
            // por el mismo thread que lo creó.
            private void AddNode(TreeNode n)
            {
    
                if (!this.IsDisposed && treeView1 != null && !treeView1.IsDisposed && this.InvokeRequired)
                {
    
                    this.Invoke(new AddNodeDelegate(AddNode), new object[] { n });
                }
                else
                {
                    this.treeView1.Nodes.Add(n);
                }
            }
    
            // Esta es una declaración de delegado para poder llamar a la función
            // AddNode en el therad que puede manipular el control treeview
            private delegate void AddNodeDelegate(TreeNode n);
            private delegate void AddNodeDelegate1(TreeNode parent, TreeNode child);
    
            // Esta función arega un nodo hijo a un nodo padre en el control
            // treeview
            private void AddNode(TreeNode parent, TreeNode child)
            {
                if (!this.IsDisposed && treeView1 != null && !treeView1.IsDisposed && this.InvokeRequired)
                {
    
                    this.Invoke(new AddNodeDelegate1(AddNode), new object[] { parent, child });
                }
                else
                {
                    parent.Nodes.Add(child);
                }
            }
    
            // Esta función llena el primer nivel del treeview
            private void FillTree(List<MyClass> list)
            {
                List<string> level1 = list.Select(l => l.Level1).Distinct().ToList<string>();
                foreach (string s1 in level1)
                {
                    TreeNode t = new TreeNode { Text = s1, Name = s1 };
                    AddNode(t);
                    Thread th = new Thread(() => FillLevel2(list, s1, t));
                    th.Start();
    
                }
            }
    
            // Esta función llena el segundo nivel del treeview
            private void FillLevel2(List<MyClass> list, string level1, TreeNode t)
            {
                if (level1.Substring(0, 2) == "18")
                    Thread.Sleep(1000);
    
                List<string> level2 = list.Where(s => s.Level1 == level1).Select(s => s.Level2).Distinct().ToList<string>();
                foreach (string s2 in level2)
                {
                    TreeNode t1 = new TreeNode() { Text = s2, Name = s2 };
                    AddNode(t, t1);
                    FillLevel3(list, level1, s2, t1);
                }
            }
    
    
            // Esta función llena el tercer nivel del treeview
            private void FillLevel3(List<MyClass> list, string level1, string level2, TreeNode t)
            {
                List<string> level3 = list.Where(s => s.Level1 == level1 && s.Level2 == level2).Select(s => s.Level3).Distinct().ToList<string>();
                foreach (string s3 in level3)
                {
                    TreeNode t1 = new TreeNode() { Text = s3, Name = s3 };
                    AddNode(t, t1);
                    FillLevel4(list, level1, level2, s3, t1);
                }
            }
    
            // Esta función llena el cuarto nivel del treeview
            private void FillLevel4(List<MyClass> list, string level1, string level2, string level3, TreeNode t)
            {
                List<string> level4 = list.Where(s => s.Level1 == level1 && s.Level2 == level2 && s.Level3 == level3).Select(s => s.Level4).ToList<string>();
                foreach (string s4 in level4)
                {
                    TreeNode t1 = new TreeNode() { Text = s4, Name = s4 };
                    AddNode(t, t1);
                }
            }
    
    
            // FORM LOAD EVENT
            private void Form1_Load(object sender, EventArgs e)
            {
                List<MyClass> list = MyClass.GetPlainList();
    
                Thread th = new Thread(() => FillTree(list));
                th.Start();
            }
        }
    }

    ¿Alguna idea? O.o


    logo osoft
    Si he contestado tu pregunta, por favor marca mi post como respuesta.
    ...Y si mi post te ha servido, márcalo como útil smile




    • Tipo cambiado Yván Ecarri viernes, 17 de agosto de 2012 12:21
    • Editado Yván Ecarri viernes, 17 de agosto de 2012 12:22
    martes, 7 de agosto de 2012 12:11