asp.net並行與多線程的理解

來源:互聯網
上載者:User

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檔案裡修改,修改<serverruntime appconcurrentrequestlimit="5000" /> ,並發要求節流數量,

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中<processmodel autoconfig="true"/>採用預設配置,我覺得應該是.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,沒有限制。這基本上就相當於對請求數量沒有限制了,同時  <processmodel

requestqueuelimit="10000"/>   加了

表示請求排隊隊列10000,在%systemroot%system32inetsrvconfigapplicationhost.config中設定,<serverruntime appconcurrentrequestlimit="10000" /> ,都是增加並行要求節流的

看一款多線程作業碼利用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
    {
        /// <summary>
        /// 應用程式的主進入點。
        /// </summary>
        [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();

            }

其實這樣就可以簡單的實現多線程了.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.