读书人

多线程查询解决办法

发布时间: 2012-06-03 16:59:40 作者: rapoo

多线程查询
本人第一次使用多线程,虽然了解了一点概念,但是由于从未有过实战经验,导致很简单的大数据量查询,添加等待窗体的多线程代码都无法实现,我的代码如下:高人就浪费一点时间帮我看看吧!

C# code
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.Data.SqlClient;using System.Threading;using System.Diagnostics;namespace ThreadApp{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        private delegate void Delemethod();        DataTable dt = null;          private void button1_Click(object sender, EventArgs e)        {                       Thread th=new Thread(new ThreadStart(delegate                 {                    SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=JH_Achive;user id=sa;password=UYENSKLAY==;");                    SqlCommand commm = conn.CreateCommand();                    conn.Open();                    commm.CommandText = "select * from t_houseregbase";                    SqlDataAdapter sa = new SqlDataAdapter(commm);                    DataSet sd = new DataSet();                    sa.Fill(sd);                    dt = sd.Tables[0];                    dgv.BeginInvoke(new Delemethod(dataBond));                                 }));            th.Start();            FormWait fw=new FormWait ();            fw.ShowDialog();            Thread.Sleep(5000);            fw.Close();            if (dt!=null&&dt.Rows.Count > 0)            {                th.Abort();                MessageBox.Show("查询完成!!!!");            }            else             {                MessageBox.Show("查询出错!!!!");            }        }        public void dataBond()         {            dgv.DataSource = dt;        }        private void button2_Click(object sender, EventArgs e)        {                    }    }}

现在的结果是,点击查询按钮之后,它会马上跳出fw窗体,然后再填充datagridview,由于ShowDialog方法之后的代码不会运行,所以fw窗体一直不关闭,只能停止调试或关闭进程。

我想要的效果就是点击查询按钮时,弹出等待窗体,等到查询线程完毕时,自动关闭等待窗体,填充datagridview。但我就是怎么也实现不了,懂的高手们帮忙看看吧,谢谢啦。

[解决办法]
这样能实现你的效果
C# code
private delegate DataTable Delemethod();private void button1_Click(object sender, EventArgs e){    this.Visible = false;    FormWait fw = new FormWait();    fw.Show();    ThreadPool.QueueUserWorkItem(param =>    {        Delemethod dt = () =>        {            SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=JH_Achive;user id=sa;password=UYENSKLAY==;");            SqlCommand commm = conn.CreateCommand();            conn.Open();            commm.CommandText = "select * from t_houseregbase";            SqlDataAdapter sa = new SqlDataAdapter(commm);            DataTable dtbl = new DataTable();            sa.Fill(dtbl);            return dtbl;        };        IAsyncResult iar = dt.BeginInvoke(null, null);        bool r = iar.AsyncWaitHandle.WaitOne(5000);        this.Invoke((MethodInvoker)(() =>        {            fw.Close();            this.Visible = true;            if (r)            {                dgv.DataSource = dt.EndInvoke(iar);            }            else            {                MessageBox.Show("查询出错!!!!");            }        }));    });}
[解决办法]
对于button1_Click方法,它只要几毫秒、“一瞬间”就执行完了,这才是多线程编程。因为子线程去执行额外的工作,而窗口线程根本不阻塞,而是干完了button1_Click方法之后立刻去用于处理窗口消息循环中的其它事件去了。
[解决办法]
C# code
楼上那些匿名函数调用法,看着真累啊,我还是使用最原始的方法吧请sp1234老大指教我怎么觉得我陷入了一种因为事件而使用委托的地步了呢?using System;using System.Collections.Generic;using System.Windows.Forms;namespace WindowsFormsApplication5{    /// <summary>    /// 定义一个加载用户信息完成的委托    /// </summary>    /// <param name="dicUserInfo"></param>    public delegate void GetUserInfoCompletedHandle(Dictionary<String, String> dicUserInfo);    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();            Control.CheckForIllegalCrossThreadCalls = false;        }        /// <summary>        /// 显示用户信息        /// </summary>        /// <param name="dicUserInfo"></param>        private void ShowUserInfo(Dictionary<String, String> dicUserInfo)        {            try            {                listBox1.SuspendLayout();                foreach (String id in dicUserInfo.Keys)                {                    listBox1.Items.Add(String.Format("{0},{1}", id, dicUserInfo[id]));                }                listBox1.ResumeLayout();            }            catch(Exception ex)            {                MessageBox.Show(ex.Message);            }        }        private void button1_Click(object sender, EventArgs e)        {            // 新开一个线程加载用户信息            UserInfo userInfo = new UserInfo();            // 加载用户信息完成后执行ShowUserInfo            userInfo.OnGetUserInfoCompleted += new GetUserInfoCompletedHandle(ShowUserInfo);            System.Threading.Thread thread = new System.Threading.Thread(userInfo.GetUserInfo);            thread.IsBackground=true;            thread.Start();        }    }    /// <summary>    /// 用户信息类    /// </summary>    public class UserInfo    {        /// <summary>        /// 加载用户信息完成事件        /// </summary>        public event GetUserInfoCompletedHandle OnGetUserInfoCompleted;        /// <summary>        /// 从数据库加载用户信息,加载完成后,触发OnGetUserInfoCompleted事件        /// </summary>        public void GetUserInfo()        {            Dictionary<String, String> retDic = new Dictionary<String, String>();            for (Int32 i = 0; i < 10000; i++)            {                retDic.Add(String.Format("id={0}", i), String.Format("name={0}", i));            }            if (OnGetUserInfoCompleted != null)            {                OnGetUserInfoCompleted(retDic);            }        }    }} 


[解决办法]
我来解释一下你不能实现的原因,我发现其他回答人总是回答不到点子上。程序员的表述能力都那么差吗?悲哀啊!!!!
1.你真的就像你自己说的,对多线程完全不懂,你甚至不知道showdialog方法会阻塞线程。
2.thread.sleep()方法阻塞线程,这里你是想阻塞UI主线程,让子线程完成之后在执行下去,对吧?
3.你不明白为什么会先弹出“查询出错”或者“查询完成”这样窗体,然后才绑定列表,对吧?
解答:
1.showdialog方法弹出模态窗体,除非在窗体内事件代码里关闭或者手动关闭,从该窗体以外的代码里在showdialog方法之后的代码将不会执行。
2.如果你阻塞了UI主线程,界面一样会假死一段时间,这完全是你考虑的错误。
3.尽管你阻塞了UI主线程,但是5秒过后,子线程还在进行大数据量查询,且委托方法执行未完成,所以会先显示“查询错误或完成”这样的提示窗体。
话外:
1.多线程就是让CUP在同一时间上宏观上实现并行处理能力。也就是说,几乎同一时间做两件或两件以上的事情,实际则是当你启动一个新的线程时,就和主线开始开始抢夺CPU的控制时间,因为在单处理器情况下,处理器会为每个线程分配处理时间,并且轮换执行多个线程。也就是说,当你阻塞线程时,则是发送指令告诉CPU暂时不要给这个线程分配处理时间。这是CPU的任务,如果你闲着蛋疼,大可以去了解下。

2.那么如何垮线程访问呢?我好像没有告诉你线程之间是彼此独立的,这句话看似很好理解,实则上,你会认为,既然是在主线程上创建了子线程,为什么会说是彼此独立的?对于此,我的理解是,不论怎么样,它们都处于同一个应用程序域,就像你的父母生了你,但是你们却都是地球人(应用程序域),你和你的父母彼此独立,你不可能使用你父亲的耳朵来听或者你母亲的眼睛来看吧?但是一旦无论谁离开了地球都无法生存。(例子可能不好,但是我没有恶意)。唯独能达到垮线程访问的方法就是你父亲听了告诉你,你母亲看了告诉你(委托及回调)。到线程里就是你在需要访问的线程里创建方法,然后在需要得到结果的线程里利用委托得到结果。

3.如果你子线程里直接使用show()方法,你会发现控件不能显示,会被该窗体之后的背景所覆盖。为什么主线程调用就不会出现这种情况?为什么子线程使用ShowDialog方法也不会出现这种情况?为什么?!!!
答:第一点,show和showdialog有几个区别,其一是show只是显示窗体,不会处理事件消息。纳尼?什么是事件消息??事件消息可以理解成要求你执行某些事件的指令。针对于加载窗体,你可以去看看windform中designer页面代码,那些就是绘制控件事件之后生成的代码。也就是说,使用Show方法仅仅绘制了窗体,而没有完全绘制控件(注意:不是没有绘制)。因为show方法没有启动消息循环机制。但是,showdialog方法却启用了消息循环机制。区别之二就是,一个是模态,一个是非模态。纳尼,什么是模态?我劝你转行吧。
第二点,在主线程中使用show方法可以正常显示窗体的原因就是它启用了消息循环机制,查看winform中program.cs中Application.Run(new Form1());方法,该方法就是启动消息循环机制的源头。我相信你在百度或谷歌的过程中,你肯定看到不少多线程的例子,都有这样的一句代码来显示窗体,原因就是如此。

4.除非你关闭进程或显示关闭,否则子线程不会因为你关闭了主线程而停止,如果它没有执行完成的话。但是主线程执行完毕之后,进程就会自动关闭。

5.关于线程池,线程监视,我就不多讲了,了解了以上,差不多就比较简单了。从繁而简。
最后,附上我改进的代码:

C# code
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.Data.SqlClient;using System.Threading;using System.Diagnostics;namespace ThreadApp{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        private delegate void Delemethod();        DataTable dt = null;          private void button1_Click(object sender, EventArgs e)        {            Thread th=new Thread(new ThreadStart(FillData));            th.Name = "MyThread";            th.Start();        }        public void dataBond()         {            dgv.DataSource = dt;        }        private void button2_Click(object sender, EventArgs e)        {            this.Close();                        Process.GetCurrentProcess().Kill();        }        public void FillData()         {            FormWait fw = new FormWait();            lblText.BeginInvoke(new Delemethod(delegate { lblText.Text = "正在查询..."; }));            this.BeginInvoke(new Delemethod(delegate            {                fw.Show();            }));            SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=JH_Achive;user id=sa;password=q1w2e3r4;");            SqlCommand commm = conn.CreateCommand();            conn.Open();            commm.CommandText = "select * from t_houseregbase";            SqlDataAdapter sa = new SqlDataAdapter(commm);            DataSet sd = new DataSet();            sa.Fill(sd);            dt = sd.Tables[0];            dgv.BeginInvoke(new Delemethod(dataBond));            lblText.BeginInvoke(new Delemethod(delegate { lblText.Text = ""; }));            this.BeginInvoke(new Delemethod(delegate            {                fw.Close();            }));        }    }} 

读书人网 >C#

热点推荐