Code
自己在做一个多线程的TCP端口扫描程序,由于要用到多线程技术,而C#对线程技术 的封装非常方便使用,且如果不做网络底层处理,C#使用套接字也非常方便,于是决定用C#.NET实现。 前面一切都很顺利,到后来要启用多线程技术,启用多个线程同时发送和接受数据,并且在收到返回信息时,根据情况对主窗体中的进度条和相关UI进行改变。在这个问题上总是出现问题:每当开始工作后,程序主窗体处于死亡状态,对用户操作没有任何反应。程序中对于扫描我是这样实现的:(部分伪代码)
public partial class Form1 : Form
{
private delegate void Method(); //委托申明,子线程条用UI中的方法
private ArrayList Config = new ArrayList();//模拟装所有端口配置
private ArrayList done = new ArrayList();//装完成端口
private long AlivedThread = 0;//当前存活线程数
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
done.Clear();//将完成项清空
toolStripProgressBar1.Value = 0;//设置初始值
Random rand = new Random(200); //下面和循环,模拟初始化端口配置项
int max=rand.Next(1000);
for (int i = 0; i < max; i++)
{
Config.Add(rand.Next());
}
toolStripProgressBar1.Minimum = 0;
toolStripProgressBar1.Maximum = Config.Count;
foreach (object item in Config)//循环扫描每项
{
again: if (Interlocked.Read(ref AlivedThread) < 10)
{
Thread ScanThread= new Thread
(new ParameterizedThreadStart(ScanFunc));
ScanThread.Start(item);
Thread.Sleep(10);
}
else
{
Thread.Sleep(10);
goto again;
}
}
}
private void ScanFunc(object item)
{
Interlocked.Increment(ref AlivedThread);
Monitor.Enter(done);
done.Add(item);
Monitor.Exit(done);
Thread.Sleep(500);
SetProgressMethod();
Interlocked.Decrement(ref AlivedThread);
}
private void SetProgressMethod()
{
MethodInvoker mi = new MethodInvoker(this.SetProgressBar);
this.BeginInvoke(mi);
Thread.Sleep(100);
}
private void SetProgressBar()
{
Monitor.Enter(done);
toolStripProgressBar1.Value = done.Count;
Monitor.Exit(done);
this.Refresh();
}
就上面代码老是出现主窗体死亡,对用户操作没有任何反应的情况。我在网上四处找,一好心网友也给了我相关代码,但和我的类似,实例只有两个线程,一个线程主线程,一个控制进度条。今天看了CSDN上愈翁的一篇文章,内容同样很简单,也好似两个线程,和我得到的代码类似。但是我看到文章下面有网友提问,作者有一句关键的回答,“线程提交给UI处理A任务,而UI正在处理B任务”,我这才回过神来。原来我的主线程中有个foreach循环,UI线程一直处理它呢,没有时间相应子线程提交任务!
于是马上对代码做了修改:新建一线程函数,将循环放到该线程中执行,修改如下:
private void LoopFun()
{
foreach (object item in Config)//循环扫描每项
{
again: if (Interlocked.Read(ref AlivedThread) < 10)
{
Thread ScanThread= new Thread
(new ParameterizedThreadStart(ScanFunc));
ScanThread.Start(item);
Thread.Sleep(10);
}
else
{
Thread.Sleep(10);
goto again;
}
}
}
然后在主线程中申明另一线程执行该函数,
Thread loop = new Thread(new ThreadStart(LoopFun));
loop.Start();
测试,程序工作时,UI完全能正常反应用户操作了!
最后,在做多线程程序时,一定要遵循费时操作不放在主线程中的原则!
自己在做一个多线程的TCP端口扫描程序,由于要用到多线程技术,而C#对线程技术 的封装非常方便使用,且如果不做网络底层处理,C#使用套接字也非常方便,于是决定用C#.NET实现。 前面一切都很顺利,到后来要启用多线程技术,启用多个线程同时发送和接受数据,并且在收到返回信息时,根据情况对主窗体中的进度条和相关UI进行改变。在这个问题上总是出现问题:每当开始工作后,程序主窗体处于死亡状态,对用户操作没有任何反应。程序中对于扫描我是这样实现的:(部分伪代码)
public partial class Form1 : Form
{
private delegate void Method(); //委托申明,子线程条用UI中的方法
private ArrayList Config = new ArrayList();//模拟装所有端口配置
private ArrayList done = new ArrayList();//装完成端口
private long AlivedThread = 0;//当前存活线程数
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
done.Clear();//将完成项清空
toolStripProgressBar1.Value = 0;//设置初始值
Random rand = new Random(200); //下面和循环,模拟初始化端口配置项
int max=rand.Next(1000);
for (int i = 0; i < max; i++)
{
Config.Add(rand.Next());
}
toolStripProgressBar1.Minimum = 0;
toolStripProgressBar1.Maximum = Config.Count;
foreach (object item in Config)//循环扫描每项
{
again: if (Interlocked.Read(ref AlivedThread) < 10)
{
Thread ScanThread= new Thread
(new ParameterizedThreadStart(ScanFunc));
ScanThread.Start(item);
Thread.Sleep(10);
}
else
{
Thread.Sleep(10);
goto again;
}
}
}
private void ScanFunc(object item)
{
Interlocked.Increment(ref AlivedThread);
Monitor.Enter(done);
done.Add(item);
Monitor.Exit(done);
Thread.Sleep(500);
SetProgressMethod();
Interlocked.Decrement(ref AlivedThread);
}
private void SetProgressMethod()
{
MethodInvoker mi = new MethodInvoker(this.SetProgressBar);
this.BeginInvoke(mi);
Thread.Sleep(100);
}
private void SetProgressBar()
{
Monitor.Enter(done);
toolStripProgressBar1.Value = done.Count;
Monitor.Exit(done);
this.Refresh();
}
就上面代码老是出现主窗体死亡,对用户操作没有任何反应的情况。我在网上四处找,一好心网友也给了我相关代码,但和我的类似,实例只有两个线程,一个线程主线程,一个控制进度条。今天看了CSDN上愈翁的一篇文章,内容同样很简单,也好似两个线程,和我得到的代码类似。但是我看到文章下面有网友提问,作者有一句关键的回答,“线程提交给UI处理A任务,而UI正在处理B任务”,我这才回过神来。原来我的主线程中有个foreach循环,UI线程一直处理它呢,没有时间相应子线程提交任务!
于是马上对代码做了修改:新建一线程函数,将循环放到该线程中执行,修改如下:
private void LoopFun()
{
foreach (object item in Config)//循环扫描每项
{
again: if (Interlocked.Read(ref AlivedThread) < 10)
{
Thread ScanThread= new Thread
(new ParameterizedThreadStart(ScanFunc));
ScanThread.Start(item);
Thread.Sleep(10);
}
else
{
Thread.Sleep(10);
goto again;
}
}
}
然后在主线程中申明另一线程执行该函数,
Thread loop = new Thread(new ThreadStart(LoopFun));
loop.Start();
测试,程序工作时,UI完全能正常反应用户操作了!
最后,在做多线程程序时,一定要遵循费时操作不放在主线程中的原则!