【www.gdgbn.com--Windows服务器】
asp教程.net并行与多线程的理解
asp.net教程并发请求数量影响的条件相当多,不考虑程序执行时间和是否被阻塞,它会受到服务器tcp(一般windows服务器好像没限制)连接数限制,iis连接数限制,clr线程池对它的限制。
服务器tcp限制,[hklmsystemcurrentcontrolsetservicestcpipparameters] “enableconnectionratelimiting” =dword:00000000, 该注册表项的值改成 “0” 即可,如果是比如windows2003服务器,一般是没有这个限制的,比如我自己的2003没有。
iis连接数限制:iis6及以下,%systemroot%system32inetsrv下的metabase.xml文件里直接修改,比如修改默认的asprequestqueuemax="3000",可以把请求排队列队设置更大,修改默认的aspprocessorthreadmax="25",把request并发请求线程设置更大等,具体可以参考相关资料,进程太多会设置到cpu切换线程要耗时,根据网上说法,并不是越大越好,但可以综合根据并发量和系统性能相关形成设置;iis7配置的地方又不一样了,%systemroot%system32inetsrvconfigapplicationhost.config文件里修改,修改
iis manager > applicationpools > advanced settings, queue length:1000修改队列长度.hklmsystemcurrentcontrolsetserviceshttpparameters maxconnections 修改更大。
.net框架方面的限制
我认为一个asp.net程序运行起来后, 不关心不同的iis版本,它们生命逻辑不一样,iis6交给的进程之前也不一样,但我认为该进程都至少由三个线程池来管理,一个clr负责不同request请求的工作线程池,一个io处理线程,还有该进程的非托管级的线程池,托管的这两个线程池的初始化和最大数量都可以配置和程序设置,非托管的线程池用于中转请求给clr处理,和有io或者其他异步操作时,处理的非托管级线程。当然托管级的线程池我们是可以作一定控制的,不同.net版本,和系统cpu数量,初始化线程池数量是不一样的,比如我现在cpu是单cpu双核系统,在.net4下一个mvc请求中执行下面代码:
int workthread = 0;
int iothread = 0;
threadpool.getmaxthreads(out workthread, out iothread);
获取到最大工作线程池和io线程池都为200,但如果根据相关资料,说该值为100 *cpu数量(双核也算多个cpu),windows程序中获取到的值将更大,说明默认系统给的线程数是不一样的。
在不同.net版本中,machine.config中
设置maxworkerthreaders和maxiothreads为100
设置maxconnection 为12*cpu数量
设置minfreethreads为88*cpu数量
设置minworkerthreads为50
最大线程数量 = (最大工作线程 * cpu数量) - 最小空闲线程, 当然还有其他一些重要的参数。
这些都是iis6和iis7经典模式下的配置文件的修改,ii7集成模式又有点不一样了,请求最大数量由下面决定:maxconcurrentrequestspercpu:限制每个cpu执行的请求的数量,即使有更多的线程可用。在.net 3.5及以前的版本中,默认值是12,在.net 4中是5000。如果设为0,没有限制;maxconcurrentthreadspercpu:限制每个cpu处理请求的线程的数量。默认值是0,没有限制。这基本上就相当于对请求数量没有限制了,同时 requestqueuelimit="10000"/> 加了 表示请求排队队列10000,在%systemroot%system32inetsrvconfigapplicationhost.config中设置, 看一款多线程操作代码利用socket 首先必须包含的两个命名空间: using system.net; using system.net.sockets; 几个常用的类:(这些东西,查下msdn就很详细了) iphostentry, dns,ipaddress,ipendpoint,还有最重要的socket ipendpoint: 这个是网络终结点,很好理解,就是网络上一个固定的地址:一个ip与一个端口的组合. 下面我还是以我以前写的一个很简单的聊天程序做示例吧, (很短代码的) form1.cs //说明下, 这个是集server与client与一体的. using system; namespace onlysocket public static ipaddress getserverip() //静态函数, 无需实例化即可调用. //或用dns.resolve()代替gethostname() private void beginlisten() //socket监听函数, 等下作为创建新线程的参数 private void btconnect_click(object sender, eventargs e) //连接按钮触发的事件: 连接server } private void btstopconnect_click(object sender, eventargs e) //停止监听 private void btexit_click(object sender, eventargs e) private void btsend_click(object sender, eventargs e) program.cs using system; namespace onlysocket { 写了半天了, 够累的了, 虽然都是很基础的东西, 我自己写的时候也复习了一边 , 呵呵. 其实多线程我自己也不是很熟练, 记得去年暑假写过一个多线程扫描器, 也不知为啥, 线程开到50以上就异常, 很郁闷的. 其实当时我就是用的new thread=thread(new threadstart(fun))实现的, 方法感觉很笨拙,呵呵. 大致代码好像是这样的吧: 先写个scan类: public class scan { try{ public scan(){ ...init... } public void scan{ ..task循环扫描... } //task结构体里面有ip, 端口, 是否已扫描标记flag} catch{} } 然后主函数里面可以这样搞: scan[] scanner = new scan[xx] thread[] thread = new thread[xx]; } 其实这样就可以简单的实现多线程了.
using system.collections.generic;
using system.componentmodel;
using system.data;
using system.drawing;
using system.text;
using system.windows.forms;
using system.net;
using system.net.sockets; //这个和上面的是使用socket必须的.
using system.io;
using system.threading; //这个是使用多线程必需的.
{
public partial class form1 : form //partial表示这块代码只是form1类的部分, form1类继承自form类
{
public form1()
{
initializecomponent(); //构造函数, 初始化容器.
}
socket sock; //定义一个socket类的对象 (默认为protected)
thread th; //定义一个thread类的对象
//
{
iphostentry ieh = dns.gethostbyname(dns.gethostname()); //不多说了, dns类的两个静态函数
return ieh.addresslist[0]; //返回address类的一个实例. 这里addresslist是数组并不奇怪,一个server有n个ip都有可能
}
{
ipaddress serverip = getserverip(); //调用本类静态函数getserverip得到本机ipaddress.
ipendpoint iep = new ipendpoint(serverip, convert.toint32(tbport.text)); //本地终结点
sock = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp); //实例化内成员sock
byte[] bytemessage = new byte[100]; //存放消息的字节数组缓冲区, 注意数组表示方法,和c不同的.
this.lbiep.text = iep.tostring();
sock.bind(iep); //socket类的一个重要函数, 绑定一个ip,
while (true) //这里弄了个死循环来监听端口, 有人会问死循环了,那程序不卡住了, 注意这只是个类, 这里还没有main函数呢.
{
try
{
sock.listen(5); //好了,sock绑定了本地终结点就可以开始监听了,5表示最大连接数为5
socket newsock = sock.accept(); //这里又有socket类的一个重要的方法:accept, 该方法接受来自外面的socket连接请求, 并返回一个socket套接字, 这个套接字就开始处理这一个client与server之间的对话
newsock.receive(bytemessage); //接受client发送过来的数据保存到缓冲区.
string msg = "from [" + newsock.remoteendpoint.tostring() + "]:" +system.text.encoding.utf8.getstring(bytemessage)+"n"; //getstring()函数将byte数组转换为string类型.
rtbtalk.appendtext(msg+"n"); //显示在文本控件里
}
catch (socketexception se) //捕捉异常,
{
lbstate.text = se.tostring(); //将其显示出来, 在此亦可以自定义错误.
}
}
}
{
btconnect.enabled = false;
btstopconnect.enabled = true;
try
{
th = new thread(new threadstart(beginlisten)); //创建一个新的线程专门用于处理监听,这句话可以分开写的,比如: threadstart ts=new threadstart(beginlisten); th=new thread (ts); 不过要注意, threadstart的构造函数的参数一定要是无参数的函数. 在此函数名其实就是其指针, 这里是委托吗?
th.start(); //启动线程
lbstate.text = "listenning...";
}
catch (socketexception se) //处理异常
{
messagebox.show(se.message, "出现问题", messageboxbuttons.ok, messageboxicon.information);
}
catch (argumentnullexception ae) //参数为空异常
{
lbstate.text = "参数错误";
messagebox.show(ae.message, "错误", messageboxbuttons.ok, messageboxicon.warning);
}
{
btstopconnect.enabled = false;
btconnect.enabled = true;
sock.close(); //关闭套接字
th.abort(); //终止监听线程
lbstate.text = "listenning stopped";
}
{
sock.close();
th.abort();
dispose(); //清理资源,就是释放内存
this.close(); //关闭对话框, 退出程序
}
{
try
{
ipaddress clientip = ipaddress.parse(tbtargetip.text); //类ipaddress的静态函数parse() :将text转化为ipaddress的一个实例.
int clientport = convert.toint32(tbport.text); //c#的这些转化函数很方便的,不像c++那样麻烦
ipendpoint clientiep = new ipendpoint(clientip, clientport); //这里用client表示不是很好....,
byte[] byte_message;
socket socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp); 实例化的时候还有很多参数的, 这个是tcp的. tcp的sockettype是stream:数据流, 如果协议类型是udp, 则是数据包传送, qq就是用的udp.
socket.connect(clientiep); //socket的又一个函数connect(ipendpoint) .连接远程套接字
byte_message = system.text.encoding.utf8.getbytes(rtbwords.text); //发现utf8可支持中文,就用之
socket.send(byte_message);
rtbtalk.appendtext("n"+"my words:" + rtbwords.text + "n");
socket.shutdown(socketshutdown.both);
socket.close();
}
catch (argumentnullexception ae)
{
messagebox.show(ae.message,"参数为空",messageboxbuttons.okcancel,messageboxicon.information);
}
catch (socketexception se)
{
messagebox.show(se.message, "出现问题", messageboxbuttons.ok, messageboxicon.information);
}
}
}
}
using system.collections.generic;
using system.windows.forms;
{
static class program
{
///
/// 应用程序的主入口点。
///
[stathread]
static void main() //这儿才是main函数
application.enablevisualstyles();
application.setcompatibletextrenderingdefault(false);
application.run(new form1());
}
}
}
for (int i = 0; i < xx;i++)
{
scanner[i] = new scan(this, i);
thread[i] = new thread(new threadstart(scanner[i].startscan));
thread[i].start();