【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中采用默认配置,我觉得应该是.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;
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;      //这个是使用多线程必需的.

namespace onlysocket
{
    public partial class form1 : form           //partial表示这块代码只是form1类的部分, form1类继承自form类
    {
        public form1()
        {
            initializecomponent();    //构造函数, 初始化容器.
        }
        socket sock;          //定义一个socket类的对象 (默认为protected)
        thread th;             //定义一个thread类的对象
        //

        public static ipaddress getserverip()        //静态函数, 无需实例化即可调用.
        {
            iphostentry ieh = dns.gethostbyname(dns.gethostname()); //不多说了, dns类的两个静态函数

             //或用dns.resolve()代替gethostname()
            return ieh.addresslist[0];                  //返回address类的一个实例. 这里addresslist是数组并不奇怪,一个server有n个ip都有可能
        }
     

        private void beginlisten()               //socket监听函数, 等下作为创建新线程的参数
        {
            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();       //将其显示出来, 在此亦可以自定义错误.
                }
            }
        }

        private void btconnect_click(object sender, eventargs e)   //连接按钮触发的事件: 连接server
        {
            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);
            }

        }

        private void btstopconnect_click(object sender, eventargs e)  //停止监听
        {
            btstopconnect.enabled = false;
            btconnect.enabled = true;
            sock.close();                     //关闭套接字
            th.abort();                         //终止监听线程
          
           lbstate.text = "listenning stopped";
        }

        private void btexit_click(object sender, eventargs e)      
        {
            sock.close();
            th.abort();
            dispose();             //清理资源,就是释放内存
            this.close();          //关闭对话框, 退出程序
        }

        private void btsend_click(object sender, eventargs e)
        {
            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);
            }
        }
      
    }
}

program.cs

using system;
using system.collections.generic;
using system.windows.forms;

namespace onlysocket
{
    static class program
    {
        ///


        /// 应用程序的主入口点。
        ///

        [stathread]
        static void main()        //这儿才是main函数

        {
            application.enablevisualstyles();
            application.setcompatibletextrenderingdefault(false);
            application.run(new form1());
        }
    }
}


 

写了半天了, 够累的了, 虽然都是很基础的东西, 我自己写的时候也复习了一边 , 呵呵.

其实多线程我自己也不是很熟练, 记得去年暑假写过一个多线程扫描器, 也不知为啥, 线程开到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];
            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();

            }

其实这样就可以简单的实现多线程了.

本文来源:http://www.gdgbn.com/caozuoxitong/28853/