新人关于线程池相关的疑问(WaitHandle.WaitAll和ThreadPool.QueueUserWorkItem)

wmsdg 2008-07-30 02:55:21
程序代码如下文,都是可以执行并符合要求的(输入主机ip,起始和结束端口,启用线程池逐端口测试tcp连接,待全部线程空闲后,主线程继续并结束)
问题1:ascan.eventX = eventXAll[i - startPort];//这里传递的是对象的地址是么?
ascan.eventX和 eventXAll[i - startPort]指向的是内存的同一地址?

问题2:WaitHandle.WaitAll方法似乎最多只能控制64个线程,怎么办?

问题3:为什么ThreadPool.QueueUserWorkItem(myWaitCB,remote);也可以用ThreadPool.QueueUserWorkItem(ascan.ScanPortWork,remote);代替


using System;
using System.Threading;
using System.Net;
using System.Net.Sockets;

public class PortScan
{
public static void Main()
{

Console.WriteLine("输入主机名称: ");
string hostName = Console.ReadLine();
IPAddress ip = IPAddress.Parse(hostName);

Console.WriteLine("输入开始扫描通信端口: ");
int startPort = int.Parse(Console.ReadLine());

Console.WriteLine("输入结束扫描通信端口: ");
int endPort = int.Parse(Console.ReadLine());
ThreadPool.SetMaxThreads(100,300);
ManualResetEvent[] eventXAll = new ManualResetEvent[(endPort-startPort+1)];//设置一个ManualResetEvent类的数组,数量为所要开启的进程数



for (int i = startPort; i <= endPort; i++)
{
IPEndPoint remote=new IPEndPoint (ip,i);
ScanPort ascan = new ScanPort();
eventXAll[i - startPort] = new ManualResetEvent(false);//初始化一个ManualResetEvent对象
ascan.eventX = eventXAll[i - startPort];//把其传递给子线程?
WaitCallback myWaitCB=new WaitCallback (ascan.ScanPortWork);
ThreadPool.QueueUserWorkItem(myWaitCB,remote);
}
//eventX.WaitOne(Timeout.Infinite, true);
WaitHandle.WaitAll(eventXAll);//等待ManualResetEvent数组中所有元素均为有效
Console.WriteLine("指定通信端口扫描完成!! ");
Console.WriteLine("请按[Enter] 离开!! ");
Console.ReadLine();
}

}
public class ScanPort
{
public ManualResetEvent eventX;//设置ManualResetEvent对象,来接收主线程传递的ManualResetEvent对象
public void ScanPortWork(object remote)
{
TcpClient myTcpClient = new TcpClient();
IPEndPoint rm = (IPEndPoint)remote;
int port = 0;
try
{
myTcpClient.Connect(rm);
Console.WriteLine("Port " + rm.Port.ToString() + " 目前打开!!");
port = rm.Port;
}
catch (SocketException)
{
Console.WriteLine("Port " + rm.Port.ToString() + "关闭!");
}
finally
{
myTcpClient.Close();
}
eventX.Set(); //设置为有效
}
}
...全文
1167 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
public static void WaitAll(this List<ManualResetEvent> ehArray)
{
while (ehArray.Count > 0)
{
WaitHandle.WaitAll(ehArray.Take(64).ToArray());
ehArray = ehArray.Skip(64).ToList();
}
}
wmsdg 2008-07-30
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 gomoku 的回复:]
ThreadPool.QueueUserWorkItem(ascan.ScanPortWork,remote);

ThreadPool.QueueUserWorkItem(new WaitCallback (ascan.ScanPortWork),remote);
编译出来的代码一样。

int i = 3 + 4;
int i = (3 + 4);
int i = 7;
编译出来的代码一样。不过不能叫它们隐式转换,实际没有转换的发生,而是编译器作了处理。
float f = i;是隐式转换


第二个问题我不理解你的意思,不知道100个从哪里来的。
不过线程池有…
[/Quote]
最大线程数是25个么?我用Thread.CurrentThread.GetHashCode()不止这个数,用任务管理器看也一样
wmsdg 2008-07-30
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 gomoku 的回复:]
ThreadPool.QueueUserWorkItem(ascan.ScanPortWork,remote);

ThreadPool.QueueUserWorkItem(new WaitCallback (ascan.ScanPortWork),remote);
编译出来的代码一样。

int i = 3 + 4;
int i = (3 + 4);
int i = 7;
编译出来的代码一样。不过不能叫它们隐式转换,实际没有转换的发生,而是编译器作了处理。
float f = i;是隐式转换


第二个问题我不理解你的意思,不知道100个从哪里来的。
不过线程池有…
[/Quote]

不知道哪些情况下编译器会帮你转换代码呢?
我问的好啰嗦阿,汗
wmsdg 2008-07-30
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 gomoku 的回复:]
ThreadPool.QueueUserWorkItem(ascan.ScanPortWork,remote);

ThreadPool.QueueUserWorkItem(new WaitCallback (ascan.ScanPortWork),remote);
编译出来的代码一样。

int i = 3 + 4;
int i = (3 + 4);
int i = 7;
编译出来的代码一样。不过不能叫它们隐式转换,实际没有转换的发生,而是编译器作了处理。
float f = i;是隐式转换


第二个问题我不理解你的意思,不知道100个从哪里来的。
不过线程池有…
[/Quote]
是这样的,我实际运行时,看到大约每1秒大概新产生1-2个线程,大概到一百多就基本不产生新的了,但线程上限还没到呢,换句话,就是新的线程调用很慢,因为不断有老的线程结束了,又回到池内被重新调用,结果就形成了一个动态平衡。我想问的是新的线程调用为什么这么慢,和我的代码有关系吗?还是系统自身的原因?
gomoku 2008-07-30
  • 打赏
  • 举报
回复
ThreadPool.QueueUserWorkItem(ascan.ScanPortWork,remote);

ThreadPool.QueueUserWorkItem(new WaitCallback (ascan.ScanPortWork),remote);
编译出来的代码一样。

int i = 3 + 4;
int i = (3 + 4);
int i = 7;
编译出来的代码一样。不过不能叫它们隐式转换,实际没有转换的发生,而是编译器作了处理。
float f = i;是隐式转换


第二个问题我不理解你的意思,不知道100个从哪里来的。
不过线程池有个最大线程数的限制,比如25个,他们重复利用。如果你打出他们的ID,可能就25个不同的号码。
wmsdg 2008-07-30
  • 打赏
  • 举报
回复
另外因为WaitHandle.WaitAll方法最多为64个线程,我试图自设一个计数器解决问题,代码如下,可以运行,但为何调用时最多时仅能调用100多个线程而不能调用更多了,但CPU明明还没满,为什么?另外请大家看一下这样写有什么问题没有?多谢了

using System;
using System.Threading;
using System.Net;
using System.Net.Sockets;

public class PortScan
{
public static void Main()
{
int worker,ioworker;
Console.WriteLine("输入主机名称: ");
string hostName = Console.ReadLine();
IPAddress ip = IPAddress.Parse(hostName);

Console.WriteLine("输入开始扫描通信端口: ");
int startPort = int.Parse(Console.ReadLine());

Console.WriteLine("输入结束扫描通信端口: ");
int endPort = int.Parse(Console.ReadLine());
ThreadPool.SetMinThreads(5, 200);
ThreadPool.SetMaxThreads(200,500);
// ManualResetEvent[] eventXAll = new ManualResetEvent[(endPort-startPort+1)];

ScanPort ascan = new ScanPort();
WaitCallback myWaitCB = new WaitCallback(ascan.ScanPortWork);

for (int i = startPort; i <= endPort; i++)
{
IPEndPoint remote=new IPEndPoint (ip,i);

// eventXAll[i - startPort] = new ManualResetEvent(false);
//ascan.eventX = eventXAll[i - startPort];

ThreadPool.QueueUserWorkItem(ascan.ScanPortWork, remote);
}
//eventX.WaitOne(Timeout.Infinite, true);
// WaitHandle.WaitAll(eventXAll);
while (ascan.count < (endPort - startPort))
{

ThreadPool.GetAvailableThreads(out worker, out ioworker);

Console.WriteLine(ascan.count+"-"+worker+"-"+ioworker);
Thread.Sleep(3000);
}
Console.WriteLine("指定通信端口扫描完成!! ");
Console.WriteLine("请按[Enter] 离开!! ");
Console.ReadLine();
}

}
public class ScanPort
{
// public ManualResetEvent eventX;
public int count=0;
public void ScanPortWork(object remote)
{
TcpClient myTcpClient = new TcpClient();
IPEndPoint rm = (IPEndPoint)remote;
int port = 0;
try
{
myTcpClient.Connect(rm);
Console.WriteLine("Port " + rm.Port.ToString() + " 目前打开!!"+Thread.CurrentThread.GetHashCode().ToString());
port = rm.Port;
}
catch (SocketException)
{
Console.WriteLine("Port " + rm.Port.ToString() + "关闭!" + Thread.CurrentThread.GetHashCode().ToString());
}
finally
{
myTcpClient.Close();
lock(this)
{
count++;
}
}
// eventX.Set();
}
}
wmsdg 2008-07-30
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 gomoku 的回复:]
问题3:为什么ThreadPool.QueueUserWorkItem(myWaitCB,remote);也可以用ThreadPool.QueueUserWorkItem(ascan.ScanPortWork,remote);代替
当你写ThreadPool.QueueUserWorkItem(ascan.ScanPortWork,remote);的时候,编译器可以理解并把它换成
ThreadPool.QueueUserWorkItem(new WaitCallback (ascan.ScanPortWork),remote);

而这句跟ThreadPool.QueueUserWorkItem(myWaitCB,remote);实际上就一样了。[/Quote]

那么什么时候可以使用类似的方式,我不特指这个方法,而是指诸如要求参数是某一类,而实际调用时用的是另一个类,这一类的情况,另外这个是不是隐式转换呢?
gomoku 2008-07-30
  • 打赏
  • 举报
回复
问题1:ascan.eventX = eventXAll[i - startPort];//这里传递的是对象的地址是么?
ascan.eventX和 eventXAll[i - startPort]指向的是内存的同一地址?

对。

问题2:WaitHandle.WaitAll方法似乎最多只能控制64个线程,怎么办?
对。这是内部规定的,一旦超出会抛出System.NotSupportedException。
可以用多个WaitHandle.WaitAll,每个等64个(记住太多的线程并不能增加你的效率)。

问题3:为什么ThreadPool.QueueUserWorkItem(myWaitCB,remote);也可以用ThreadPool.QueueUserWorkItem(ascan.ScanPortWork,remote);代替
当你写ThreadPool.QueueUserWorkItem(ascan.ScanPortWork,remote);的时候,编译器可以理解并把它换成
ThreadPool.QueueUserWorkItem(new WaitCallback (ascan.ScanPortWork),remote);

而这句跟ThreadPool.QueueUserWorkItem(myWaitCB,remote);实际上就一样了。
GabrielCNMao 2008-07-30
  • 打赏
  • 举报
回复
幫頂

110,538

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧