2011-08-07 57 views
0

我有一个表单,并没有control.I试图从数据库中获取控件,所以我的项目是慢我认为我可以使用线程,但我得到一个错误。多线程错误

void Form_Load(object sender,EventArgs e) 
    { 

    SqlDataAdapter adap=new SqlDataAdapter("Select * from MyControls"); 
    DataTable dt=new DataTable(); 

    adap.Fiil(dt); 

    foreach(DataRow dr in dt.Rows) 
    { 
     ThreadStart ts=delegate{ Sample1(dr) }; 
Thread th=new Thread(ts); 
th.start(); 

    } 

    } 

    public void Sample1(DataRow dr) 
    { 
    this.Invoke(new AddControlsDelegate(AddControls),new object[] {dr }); 
    } 
    public void AddControls(DataRow dr) 
    { 
    TextBox tx=new TextBox(); 
    tx.Name=dr["Id"].ToString(); 
    this.Controls.Add(tx); 

    } 

public delegate void AddControlsDelegate(DataRow dr); 

我试着用这个代码。但它不work.It加入相同的控制时间两次,3次,4次

哪里是我的错吗? 感谢

回答

2

您关闭了循环变量:

foreach(DataRow dr in dt.Rows) 
{ 
    ThreadStart ts=delegate{ Sample1(dr) }; 
    ... 
} 

你正在创建循环变量,不其当时值的开合 - 因为线程将只开始了很短的时间后,循环已完成,每个线程将使用当时的循环变量的值,这将是最后一行。

在循环中而不是创建一个局部变量,并在您的代理使用它:

foreach(DataRow dr in dt.Rows) 
{ 
    DataRow currentRow = dr; 
    ThreadStart ts=delegate{ Sample1(currentRow) }; 
    Thread th=new Thread(ts); 
    th.start(); 
} 

另见"Closing over the loop variable considered harmful"

+0

它的工作非常感谢lot.But控制一定要来“ID” asceding 有时它不来asceding – YardimaIhtiyaciOlan

+1

也许你应该改变SQL和包括'ORDER BY Id' ... – Yahia

+0

@YardimaIhtiyaciOlan:除了在SQL中排序时,不能控制线程实际执行的时间 - 顺序不能保证,要实现这一点,您需要一种不同的方法。此外,您当前的代码没有多大意义 - 使用多线程的开销并不值得您实际执行的操作。 – BrokenGlass

0

除了您已经创建了一个循环变量有一对夫妇一个封闭的事实与您的一般方法相关的主要问题。

首先,为DataTable中的每一行创建一个线程不是一个好主意。事情是创建和启动线程是花费和消耗大量的资源。您需要明智地选择创建线程的方式和时间。例如,运行10,000个线程(假设DataTable中有10,000个相应的行)不会很好地工作。

但是,即使您使用ThreadPool通过QueueUserWorkItem,异步委托或通过启动新的Task转移到更理智的方法,但还有另一个大问题。你打算在另一个线程上发生的操作被封送回UI线程。在这种情况下使用线程绝对没有用处。事实上,它使所有事情变得更糟,因为线程根本没有任何有用的工作。而且它需要通过Control.Invoke进行昂贵的编组操作才能完成任何事情。换句话说,你的UI线程告诉一个工作者线程做一些事情,工作线程转过来说“我不想,你这样做”。

更好的方法是执行SQL命令并在另一个线程上填充DataTable。完成后,您可以将所有这些TextBox控件添加到UI线程的表单中。实际上,使用UI元素做任何事情都必须从UI线程完成,因此您在这里确实没有多少可接受的选项。