积极答复者
多线程 返回值 和停止的问题

问题
-
需求如下:画面上 2个Button 一个是"Start" 另外一个是"Stop" 现在点击 "Strat"按钮 使用多线程查询指定网段内的信息,由于处理比较
耗费时间 使用多线程处理,把查询到的 结果一个DataTable返回给 画面用DataGridView显示出来,由于使用了多线程因此每个子线程执行的时间消耗是不一样的,因此必须让主线程等待,所有的子线程执行结束后在把每个子线程收集到的结果合并到一个DataTable里面返回给画面。这个过程内如果 觉得查询时间长点击"Stop"按钮 停止掉当前动作。这就是我提这个问题的需求,解决线程同步和停止线程。
以下是我的暂时代码
public void Polling()
{
try
{
...........
Thread[] mythread = new Thread[Count];
for (int i=0; i <Count;i ++)
{
mythread = new Thread(new ParameterizedThreadStart(gatherOnlineTime));
....................
mythread.Start(obj);
}
for (int i = 0; i < dtIPSegment.Rows.Count; i++)
{
while (mythread.ThreadState == ThreadState.Running)
{
Thread.Sleep(2000);
}
}
}
catch
{
..............
}
现在我的这段代码可以实现 返回结果值 显示。但是这样程序运行得时候 会像死机一样,因此 界面上的 "Stop"按钮点击不了。
我现在主要觉得问题在
for (int i = 0; i < dtIPSegment.Rows.Count; i++)
{
while (mythread.ThreadState == ThreadState.Running)
{
Thread.Sleep(2000);
}
}
在这里 但是 现在 我想让 主线程 等待 子线程 只找到了 这个方法。
答案
-
你好!这里你可以使用多个 backgroundworker 控件来代替你目前的方式。比如下面的示例。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Threading; namespace WindowsFormsApplication9 { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.InitUI(); } #region " InitUI " private System.Windows.Forms.DataGridView dataGridView1; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Button btnEnd; private System.Windows.Forms.Button btnStart; private BindingSource bs; private DataTable dt; private void InitUI() { this.dataGridView1 = new System.Windows.Forms.DataGridView(); this.panel1 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel(); this.btnEnd = new System.Windows.Forms.Button(); this.btnStart = new System.Windows.Forms.Button(); this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill; this.panel1.Controls.Add(this.dataGridView1); this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; this.panel2.Controls.Add(this.btnEnd); this.panel2.Controls.Add(this.btnStart); this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom; this.btnEnd.Location = new System.Drawing.Point(149, 7); this.btnEnd.Name = "btnEnd"; this.btnEnd.Size = new System.Drawing.Size(75, 26); this.btnEnd.Text = "End"; this.btnEnd.Click += new System.EventHandler(this.btnEnd_Click); this.btnStart.Location = new System.Drawing.Point(68, 7); this.btnStart.Name = "btnStart"; this.btnStart.Size = new System.Drawing.Size(75, 26); this.btnStart.Text = "Start"; this.btnStart.Click += new System.EventHandler(this.btnStart_Click); this.Controls.Add(this.panel2); this.Controls.Add(this.panel1); this.Name = "Form1"; this.Text = "Form1"; this.dt = new DataTable(); this.dt.Columns.Add("Id"); this.bs = new BindingSource(this.dt, null); this.dataGridView1.DataSource = this.bs; } #endregion private object lockObj = new object(); private bool hasFinish = false; public bool HasFinish { get { return hasFinish; } set { lock (lockObj) { hasFinish = value; } } } private void btnStart_Click(object sender, EventArgs e) { if (this.HasFinish) this.HasFinish = false; if (!this.HasFinish) this.Run(5); } private void btnEnd_Click(object sender, EventArgs e) { if (!this.HasFinish) this.HasFinish = true; } /// <summary> /// 启动多个线程获取数据 /// </summary> /// <param name="fThreadCount">需要启动的线程数</param> private void Run(Int32 fThreadCount) { for (int i = 0; i < fThreadCount; i++) { using (BackgroundWorker backgroundWorker = new BackgroundWorker()) { DataTable dataFromServer = this.dt.Clone(); backgroundWorker.DoWork += new DoWorkEventHandler(delegate(object o, DoWorkEventArgs workerEventArgs) { while (dataFromServer.Rows.Count < 5) { dataFromServer.Rows.Add(new object[] { Guid.NewGuid().ToString() }); Thread.Sleep(100); } // 获取完成 // this.HasFinish = true; }); backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate(object o, RunWorkerCompletedEventArgs e) { if (dataFromServer.Rows.Count > 0) { // 获取部分数据后将这部分数据显示到界上 this.dt.Merge(dataFromServer); this.dataGridView1.Sort(this.dataGridView1.Columns[0], ListSortDirection.Descending); this.Text = String.Format("RowCount:{0}", this.dt.Rows.Count); } // 如果整个数据还未获取完成,继续开启一个线程来获取。 if (!this.HasFinish) this.Run(1); }); backgroundWorker.RunWorkerAsync(); } } } } }
知识改变命运,奋斗成就人生!- 已编辑 肖小勇Moderator 2009年11月19日 3:40
- 已建议为答案 Raymond TangModerator 2009年11月19日 4:47
- 已标记为答案 Riquel_DongModerator 2009年11月23日 10:02
-
你好!
像这样的需求一般不用阻塞主线程,你阻塞了界面一定没有响应了!
所以,你直接用另外一个线程查询数据,获得结果后,也用这个线程来更新DataGridView就可以了!
你上面的做法和单线程效果一样了,没有体现出多线程的优势啊!
周雪峰- 已标记为答案 Riquel_DongModerator 2009年11月23日 10:02
全部回复
-
你好!这里你可以使用多个 backgroundworker 控件来代替你目前的方式。比如下面的示例。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Threading; namespace WindowsFormsApplication9 { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.InitUI(); } #region " InitUI " private System.Windows.Forms.DataGridView dataGridView1; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.Button btnEnd; private System.Windows.Forms.Button btnStart; private BindingSource bs; private DataTable dt; private void InitUI() { this.dataGridView1 = new System.Windows.Forms.DataGridView(); this.panel1 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel(); this.btnEnd = new System.Windows.Forms.Button(); this.btnStart = new System.Windows.Forms.Button(); this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill; this.panel1.Controls.Add(this.dataGridView1); this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; this.panel2.Controls.Add(this.btnEnd); this.panel2.Controls.Add(this.btnStart); this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom; this.btnEnd.Location = new System.Drawing.Point(149, 7); this.btnEnd.Name = "btnEnd"; this.btnEnd.Size = new System.Drawing.Size(75, 26); this.btnEnd.Text = "End"; this.btnEnd.Click += new System.EventHandler(this.btnEnd_Click); this.btnStart.Location = new System.Drawing.Point(68, 7); this.btnStart.Name = "btnStart"; this.btnStart.Size = new System.Drawing.Size(75, 26); this.btnStart.Text = "Start"; this.btnStart.Click += new System.EventHandler(this.btnStart_Click); this.Controls.Add(this.panel2); this.Controls.Add(this.panel1); this.Name = "Form1"; this.Text = "Form1"; this.dt = new DataTable(); this.dt.Columns.Add("Id"); this.bs = new BindingSource(this.dt, null); this.dataGridView1.DataSource = this.bs; } #endregion private object lockObj = new object(); private bool hasFinish = false; public bool HasFinish { get { return hasFinish; } set { lock (lockObj) { hasFinish = value; } } } private void btnStart_Click(object sender, EventArgs e) { if (this.HasFinish) this.HasFinish = false; if (!this.HasFinish) this.Run(5); } private void btnEnd_Click(object sender, EventArgs e) { if (!this.HasFinish) this.HasFinish = true; } /// <summary> /// 启动多个线程获取数据 /// </summary> /// <param name="fThreadCount">需要启动的线程数</param> private void Run(Int32 fThreadCount) { for (int i = 0; i < fThreadCount; i++) { using (BackgroundWorker backgroundWorker = new BackgroundWorker()) { DataTable dataFromServer = this.dt.Clone(); backgroundWorker.DoWork += new DoWorkEventHandler(delegate(object o, DoWorkEventArgs workerEventArgs) { while (dataFromServer.Rows.Count < 5) { dataFromServer.Rows.Add(new object[] { Guid.NewGuid().ToString() }); Thread.Sleep(100); } // 获取完成 // this.HasFinish = true; }); backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate(object o, RunWorkerCompletedEventArgs e) { if (dataFromServer.Rows.Count > 0) { // 获取部分数据后将这部分数据显示到界上 this.dt.Merge(dataFromServer); this.dataGridView1.Sort(this.dataGridView1.Columns[0], ListSortDirection.Descending); this.Text = String.Format("RowCount:{0}", this.dt.Rows.Count); } // 如果整个数据还未获取完成,继续开启一个线程来获取。 if (!this.HasFinish) this.Run(1); }); backgroundWorker.RunWorkerAsync(); } } } } }
知识改变命运,奋斗成就人生!- 已编辑 肖小勇Moderator 2009年11月19日 3:40
- 已建议为答案 Raymond TangModerator 2009年11月19日 4:47
- 已标记为答案 Riquel_DongModerator 2009年11月23日 10:02
-
你好!
像这样的需求一般不用阻塞主线程,你阻塞了界面一定没有响应了!
所以,你直接用另外一个线程查询数据,获得结果后,也用这个线程来更新DataGridView就可以了!
你上面的做法和单线程效果一样了,没有体现出多线程的优势啊!
周雪峰- 已标记为答案 Riquel_DongModerator 2009年11月23日 10:02
-
To X.X.Y Thank you 有几个问题 不懂,询问一下。
1, lockObj 这个变量 在程序里面有什么用,看了上面的代码感觉 只是 在Lock部分用了。
2,代码 while (dataFromServer.Rows.Count < 5)
{
dataFromServer.Rows.Add(new object[] { Guid.NewGuid().ToString() });
Thread.Sleep(100);
}
这个部分 按照我的理解 如果有这种 判断的话 画面上的鼠标会一直处于漏斗形状,不可以点击 任何按钮,而且这个时间的设置
根据程序运行时间的不同 里面的数值 会变化的的吧,比如我的线程执行的时候 有的可能要20多秒,有的可能要1分钟,这个地方
该怎么处理呢?您写的这个Demo 因为 仅仅是处理了一个字段的数据,所以可以设置成 Thread.Sleep(100); 但是 我在程序里面
该怎么设置呢?根据我目前测试结果来看 如果写成 Thread.Sleep(Time); 这样形式的时候,程序的鼠标 没有办法点击该画面上的任何按钮直到这个程序执行结束。 -
1 lock 即锁解决多线程中资源共享访问2 其实上面的例子效果就是你需要的。首先要了解 BackgroundWorker.DoWork 事件中的代码块是运行在一个事件中的,Dowork 事件结果后会加调 RunWorkerCompleted 事件。using (BackgroundWorker backgroundWorker = new BackgroundWorker()){// 复制表结构DataTable dataFromServer = this.dt.Clone();backgroundWorker.DoWork += new DoWorkEventHandler(delegate(object o, DoWorkEventArgs workerEventArgs){// 模拟从服务器获取数据。// 你可以将这部分代码换成你获取数据的代码。while (dataFromServer.Rows.Count < 5){dataFromServer.Rows.Add(new object[] { Guid.NewGuid().ToString() });Thread.Sleep(100);}// 获取完成// this.HasFinish = true;});backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate(object o, RunWorkerCompletedEventArgs e){// 数据获取完成,将获取的数据合并到 dt 中if (dataFromServer.Rows.Count > 0){// 获取部分数据后将这部分数据显示到界上this.dt.Merge(dataFromServer);this.dataGridView1.Sort(this.dataGridView1.Columns[0], ListSortDirection.Descending);this.Text = String.Format("RowCount:{0}", this.dt.Rows.Count);}// 如果整个数据还未获取完成,继续开启一个线程来获取。if (!this.HasFinish)this.Run(1);});backgroundWorker.RunWorkerAsync();}
知识改变命运,奋斗成就人生!