多线程查询
本人第一次使用多线程,虽然了解了一点概念,但是由于从未有过实战经验,导致很简单的大数据量查询,添加等待窗体的多线程代码都无法实现,我的代码如下:高人就浪费一点时间帮我看看吧!
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) { } }}
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方法之后立刻去用于处理窗口消息循环中的其它事件去了。
[解决办法]
楼上那些匿名函数调用法,看着真累啊,我还是使用最原始的方法吧请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.关于线程池,线程监视,我就不多讲了,了解了以上,差不多就比较简单了。从繁而简。
最后,附上我改进的代码:
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(); })); } }}