c#winform程序,datagridview的数据源来自一个DataTable,但获取DataTable需要一定的时间,使用单线程的话,获取得DataTable后再显示到datagridview,会大大的降低用户体验。现在考虑使用多线程,用一个线程T1去获取DataTable,主线程sleep 1秒左右,再去读取DataTable,DataTable由T1动态更新。求大神解惑,这种方法可行吗?如果可行,如何实现?有例子更好。非常感谢。

解决方案 »

  1.   

    主线程不能sleep,不然就白多线程了。
    sleep使得线程挂起,如果是主线程挂起,那么程序就假死了。
      

  2.   

    主线程不要sleep,不然和没多线程没什么区别,照样会导致用户界面无响应
    多线程更新用户界面的例子可以看这里
    http://blog.csdn.net/niewq/article/details/5607821
      

  3.   

    其实用后台线程控件BackgroundWorker还是挺方便的。
      

  4.   

    主线程并不需要等待,在触发读取的地方加上下面的代码,读取完毕后自动加载到 dataGridView1 中
    ThreadPool.QueueUserWorkItem(p =>
    {
        DataTable dtbl = new DataTable();
        // 读 dtbl 
        // 读完后下面的代码给 dataGridView1 赋值
        this.Invoke((MethodInvoker)(() => dataGridView1.DataSource = dtbl));
    });
      

  5.   

    不可行,sleep 1秒,你能保证任务完成吗,线程中加载完再操作grid,用线程+委托        DataSet ds = new DataSet(); //全局dataset
            public delegate void GetData();
            private void BindData()
            {
                if (dataGridView1.InvokeRequired) //如果是线程中访问的
                {
                    this.BeginInvoke(new GetData(BindData));
                    return;
                }
                dataGridView1.DataSource = ds.Tables["stuff"];
            }
            private void Foo()
            {
                OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data source=c:\\txl.mdb");
                con.Open();
                string sql = "select * from stuff";
                OleDbDataAdapter da = new OleDbDataAdapter(sql, con);
                ds = new DataSet();
                da.Fill(ds, "stuff");//填充到ds
                BindData();//执行绑定
            }
            private void button8_Click(object sender, EventArgs e)
            {
    //创建线程,并执行Foo
                Thread th = new Thread(new ThreadStart(Foo));
                th.Start();
            }
      

  6.   

    这个不需要另外开线程吧
    不是有BeginXXXX之类的异步方法吗
      

  7.   

    用委托的beginInvoke  避免管理线程
      --需要注意 UI元素 需要在主线程才能操作,DataTable查询完成后需要调用Form上的Invoke函数,切换线程。  
      

  8.   

    我之前的想法是,datagridview是按需显示的,用户拖动到什么位置,就动态显示到什么位置,在CellValueNeeded中加了代码实现的。主线程sleep 1秒后,T1线程的Datatable应该已经加载了足够多的行,可以在datagridview中显示开始的一部分数据。然后随着用户滚动条,来动态读取已经加载了更多的数据的Datatable显示。
      

  9.   

    感谢回复,datatable的读取需要大量时间,所以读取完毕后再加载到dataGridView1会让用户等待很久,我是想多开一个线程T1,读取部分数据后即在前台显示给客户,T1在后台继续读取数据,直到读取完毕,让用户几乎感觉不到读取等待。
      

  10.   

    可以考虑边显示边读取,T1 线程不断读取,读出一定数量就把这部分放 DataGridView 上显示,直到读完,读取代码用 OleDbDataReader
      

  11.   

    恩,这是一个方法,读取一部分数据后,绑定下datagridview的数据源,然后继续读取,就是不知道第二次对datagridview的数据源绑定的时候,会不会导致datagridview的刷新?
      

  12.   

    这要主线程也不需要等待,界面是间隔刷新数据,后台读取不用 DataAdapter.Fill,用 DataReader 方式
      

  13.   

    不通过设置 DataSource ,直接增加行,不会刷新
      

  14.   

    在代码执行的时候将鼠标状态设置为等待状态,如果你希望用户在这个时间里不操作的话,
    然后可能还需要让控件成为锁定状态,避免乱点,导致你逻辑出问题。然后开启线程
    执行读取数据
    将数据赋值给DATATABLE的方法写在新开启的线程里就行了
    然后就完事了。。
      

  15.   

    我的datagridview是用了虚模式,
    private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
            {
                e.Value = DataTable.Rows[e.RowIndex][e.ColumnIndex];
            }
    虚模式下我不知道如何动态更新datagridview的显示了。它没法直接增加行
      

  16.   

    代码太长,datatable是从二进制文件中经过一定的解码转换读取的,数据量巨大,有可能达到上千列和上万行,所以这个datatable的读取时间也很长,只能使用datagridview的虚模式,否则读取到datatable后再绑定到datagridview的时间也有点长,用了虚模式几乎看不到数据的加载时间。
      

  17.   

    下面是部分代码,这个问题把我脑袋都搞晕了,导致整个帖子有点乱,见谅。
    DataTable TableData;
    private void Detail_Load(object sender, EventArgs e)
    {
      Thread ReplayFlightData;
      ThreadStart StartReplayFlightData = new ThreadStart(getTableData);
      ReplayFlightData = new Thread(StartReplayFlightData);
      ReplayFlightData.Start();            Thread.Sleep(1000);
                //sleep1秒后,调试发现TableData的行数仍然为0?
                if (TableData.Rows.Count == 0)
                {
                    Thread.Sleep(1000);
                }
                else
                {
                    int c_intColumnCount = TableData.Columns.Count;
                    int Column_Width = 60000 / (c_intColumnCount); //datagridview的最大宽度为65535,但考虑到留出一定的裕度,设为60000
                    for (int i = 0; i < c_intColumnCount; i++)
                    {
                        if (c_intColumnCount > 500)
                        {
                            DataGridViewColumn l_dgvClmn = new DataGridViewTextBoxColumn();
                            //l_dgvClmn.Width = 2;
                            l_dgvClmn.Name = TableData.Columns[i].ColumnName;
                            l_dgvClmn.HeaderText = TableData.Columns[i].Caption;
                            try
                            {
                                dataGridView1.Columns.Add(l_dgvClmn);
                                dataGridView1.Columns[i].FillWeight = Column_Width;
                                //dataGridView1.Columns[i].Width = 2;
                            }
                            catch (Exception)
                            {
                                MessageBox.Show("显示参数太多,表格显示宽度不能超过65535像素,部分未能显示。");
                                break;
                            }
                        }
                        else
                        {
                            DataGridViewColumn l_dgvClmn = new DataGridViewTextBoxColumn();
                            l_dgvClmn.Name = TableData.Columns[i].ColumnName;
                            l_dgvClmn.HeaderText = TableData.Columns[i].Caption;
                            dataGridView1.Columns.Add(l_dgvClmn);
                        }
                    }                dataGridView1.VirtualMode = true;
                    dataGridView1.RowCount = TableData.Rows.Count;
                    //冻结前三行
                    dataGridView1.Rows[3].Frozen = true;
      }
    }
    private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
      {
      e.Value = DataTable.Rows[e.RowIndex][e.ColumnIndex];
      }
    //获取datatable函数,代码太长,省略,是一行一行获取到datatable的
    private void getTableData()        
    {
               //省略 
    }
      

  18.   

    那更容易,解码的过程放在 T1 线程中,解出一部分数据,填入 DataTable 之后,就设置 DataGridView.RowCount += 那部分数据的数量,CellValueNeeded 事件里还是原先的代码
      

  19.   

    Detail_Load 就设置开启虚模式,启动解码线程调用 getTableData ,getTableData 执行过程中设置 dataGridView1.RowCount
      

  20.   

    利用:回调,或是事件。(即:观察者模式解决)如果说业务逻辑有多种多样,也可以使用:任务队列,一个线程push,一个线程pop。