青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 297,  comments - 15,  trackbacks - 0
異步IO、APC、IO完成端口、線程池與高性能服務器
異步IO、APC、IO完成端口、線程池與高性能服務器之一 異步IO

背景:輪詢 PIO DMA 中斷

    早期IO設備的速度與CPU相比,還不是太懸殊。CPU定時輪詢一遍IO設備,看看有無處理要求,有則加以處理,完成后返回繼續工作。至今,軟盤驅動器還保留著這種輪詢工作方式。
    隨 著CPU性能的迅速提高,這種效率低下的工作方式浪費了大量的CPU時間。因此,中斷工作方式開始成為普遍采用的技術。這種技術使得IO設備在需要得到服 務時,能夠產生一個硬件中斷,迫使CPU放棄目前的處理任務,進入特定的中斷服務過程,中斷服務完成后,再繼續原先的處理。這樣一來,IO設備和CPU可 以同時進行處理,從而避免了CPU等待IO完成。
    早期數據的傳輸方式主要是PIO(程控IO)方式。通過對IO地址編程方式的方式來傳輸 數據。比如串行口,軟件每次往串行口上寫一個字節數據,串口設備完成傳輸任務后,將會產生一個中斷,然后軟件再次重復直到全部數據發送完成。性能更好的硬 件設備提供一個FIFO(先進先出緩沖部件),可以讓軟件一次傳輸更多的字節。
    顯然,使用PIO方式對于高速IO設備來說,還是占用了太 多的CPU時間(因為需要通過CPU編程控制傳輸)。而DMA(直接內存訪問)方式能夠極大地減少CPU處理時間。CPU僅需告訴DMA控制器數據塊的起 始地址和大小,以后DMA控制器就可以自行在內存和設備之間傳輸數據,其間CPU可以處理其他任務。數據傳輸完畢后將會產生一個中斷。

同步文件IO和異步文件IO

下面摘抄于MSDN《synchronous file I/O and asynchronous file I/O》。
有兩種類型的文件IO同步:同步文件IO和異步文件IO。異步文件IO也就是重疊IO。
在同步文件IO中,線程啟動一個IO操作然后就立即進入等待狀態,直到IO操作完成后才醒來繼續執行。而異步文件IO方式中,線程發送一個IO請求到內核,然后繼續處理其他的事情,內核完成IO請求后,將會通知線程IO操作完成了。
 
如果IO請求需要大量時間執行的話,異步文件IO方式可以顯著提高效率,因為在線程等待的這段時間內,CPU將會調度其他線程進行執行,如果沒 有其他線程需要執行的話,這段時間將會浪費掉(可能會調度操作系統的零頁線程)。如果IO請求操作很快,用異步IO方式反而還低效,還不如用同步IO方 式。
    同步IO在同一時刻只允許一個IO操作,也就是說對于同一個文件句柄的IO操作是序列化的,即使使用兩個線程也不能同時對同一個文件句柄同時發出讀寫操作。重疊IO允許一個或多個線程同時發出IO請求。
    異步IO在請求完成時,通過將文件句柄設為有信號狀態來通知應用程序,或者應用程序通過GetOverlappedResult察看IO請求是否完成,也可以通過一個事件對象來通知應用程序。

參考書目

1,    MSDN Library 
2,    《Windows高級編程指南》
3,    《Windows核心編程》
4,    《Windows 2000 設備驅動程序設計指南》

異步IO、APC、IO完成端口、線程池與高性能服務器之二 APC

    Alertable IO(告警IO)提供了更有效的異步通知形式。ReadFileEx / WriteFileEx在發出IO請求的同時,提供一個回調函數(APC過程),當IO請求完成后,一旦線程進入可告警狀態,回調函數將會執行。
    以下五個函數能夠使線程進入告警狀態:
    SleepEx
    WaitForSingleObjectEx
    WaitForMultipleObjectsEx
    SignalObjectAndWait
    MsgWaitForMultipleObjectsEx
    線 程進入告警狀態時,內核將會檢查線程的APC隊列,如果隊列中有APC,將會按FIFO方式依次執行。如果隊列為空,線程將會掛起等待事件對象。以后的某 個時刻,一旦APC進入隊列,線程將會被喚醒執行APC,同時等待函數返回WAIT_IO_COMPLETION。
    QueueUserAPC可以用來人為投遞APC,只要目標線程處于告警狀態時,APC就能夠得到執行。
    使用告警IO的主要缺點是發出IO請求的線程也必須是處理結果的線程,如果一個線程退出時還有未完成的IO請求,那么應用程序將永遠丟失IO完成通知。然而以后我們將會看到IO完成端口沒有這個限制。
    下面的代碼演示了QueueUserAPC的用法。

/************************************************************************/
/* APC Test.                                                            */
/************************************************************************/

DWORD WINAPI WorkThread(PVOID pParam)
{
    HANDLE Event = (HANDLE)pParam;

    for(;;)
    {
        DWORD dwRet = WaitForSingleObjectEx(Event, INFINITE, TRUE);
        if(dwRet == WAIT_OBJECT_0)
            break;
        else if(dwRet == WAIT_IO_COMPLETION)
            printf("WAIT_IO_COMPLETION\n");
    }

    return 0;
}

VOID CALLBACK APCProc(DWORD dwParam)
{
    printf("%s", (PVOID)dwParam);
}

void TestAPC(BOOL bFast)
{
    HANDLE QuitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    HANDLE hThread = CreateThread(NULL,
        0,
        WorkThread,
        (PVOID)QuitEvent,
        0,
        NULL);

    Sleep(100); // Wait for WorkThread initialized.

    for(int i=5; i>0; i--)
    {
        QueueUserAPC(APCProc, hThread, (DWORD)(PVOID)"APC here\n");

        if(!bFast)
            Sleep(1000);
    }

    SetEvent(QuitEvent);
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
}


參考書目

1,    MSDN Library 
2,    《Windows高級編程指南》
3,    《Windows核心編程》
4,    《Windows 2000 設備驅動程序設計指南》


異步IO、APC、IO完成端口、線程池與高性能服務器之三 IO完成端口

IO完成端口

下面摘抄于MSDN《I/O Completion Ports》,smallfool翻譯,原文請參考CSDN文檔中心文章《I/O Completion Ports》, http://dev.csdn.net/Develop/article/29%5C29240.shtm 。
I/O 完成端口是一種機制,通過這個機制,應用程序在啟動時會首先創建一個線程池,然后該應用程序使用線程池處理異步I/O請求。這些線程被創建的唯一目的就是 用于處理I/O請求。對于處理大量并發異步I/O請求的應用程序來說,相比于在I/O請求發生時創建線程來說,使用完成端口(s)它就可以做的更快且更有 效率。
CreateIoCompletionPort函數會使一個I/O完成端口與一個或多個文件句柄發生關聯。當與一個完成端口相關的文件句柄 上啟動的異步I/O操作完成時,一個I/O完成包就會進入到該完成端口的隊列中。對于多個文件句柄來說,這種機制可以用來把多文件句柄的同步點放在單個對 象中。(言下之意,如果我們需要對每個句柄文件進行同步,一般而言我們需要多個對象(如:Event來同步),而我們使用IO Complete Port 來實現異步操作,我們可以同多個文件相關聯,每當一個文件中的異步操作完成,就會把一個complete package放到隊列中,這樣我們就可以使用這個來完成所有文件句柄的同步)
調用GetQueuedCompletionStatus函數,某 個線程就會等待一個完成包進入到完成端口的隊列中,而不是直接等待異步I/O請求完成。線程(們)就會阻塞于它們的運行在完成端口(按照后進先出隊列順序 的被釋放)。這就意味著當一個完成包進入到完成端口的隊列中時,系統會釋放最近被阻塞在該完成端口的線程。
調用GetQueuedCompletionStatus,線程就會將會與某個指定的完成端口建立聯系,一直延續其該線程的存在周期,或被指定了不同的完成端口,或者釋放了與完成端口的聯系。一個線程只能與最多不超過一個的完成端口發生聯系。
完 成端口最重要的特性就是并發量。完成端口的并發量可以在創建該完成端口時指定。該并發量限制了與該完成端口相關聯的可運行線程的數目。當與該完成端口相關 聯的可運行線程的總數目達到了該并發量,系統就會阻塞任何與該完成端口相關聯的后續線程的執行,直到與該完成端口相關聯的可運行線程數目下降到小于該并發 量為止。最有效的假想是發生在有完成包在隊列中等待,而沒有等待被滿足,因為此時完成端口達到了其并發量的極限。此時,一個正在運行中的線程調用 GetQueuedCompletionStatus時,它就會立刻從隊列中取走該完成包。這樣就不存在著環境的切換,因為該處于運行中的線程就會連續不 斷地從隊列中取走完成包,而其他的線程就不能運行了。
對于并發量最好的挑選值就是您計算機中CPU的數目。如果您的事務處理需要一個漫長的計算時間,一個比較大的并發量可以允許更多線程來運行。雖然完成每個事務處理需要花費更長的時間,但更多的事務可以同時被處理。對于應用程序來說,很容易通過測試并發量來獲得最好的效果。
PostQueuedCompletionStatus函數允許應用程序可以針對自定義的專用I/O完成包進行排隊,而無需啟動一個異步I/O操作。這點對于通知外部事件的工作者線程來說很有用。
在沒有更多的引用針對某個完成端口時,需要釋放該完成端口。該完成端口句柄以及與該完成端口相關聯的所有文件句柄都需要被釋放。調用CloseHandle可以釋放完成端口的句柄。

下面的代碼利用IO完成端口做了一個簡單的線程池。

/************************************************************************/
/* Test IOCompletePort.                                                 */
/************************************************************************/

DWORD WINAPI IOCPWorkThread(PVOID pParam)
{
    HANDLE CompletePort = (HANDLE)pParam;
    PVOID UserParam;
    WORK_ITEM_PROC UserProc;
    LPOVERLAPPED pOverlapped;
    
    for(;;)
    {
        BOOL bRet = GetQueuedCompletionStatus(
            CompletePort,
            (LPDWORD)&UserParam,
            (LPDWORD)&UserProc,
            &pOverlapped,
            INFINITE);

        _ASSERT(bRet);

        if(UserProc == NULL) // Quit signal.
            break;

        // execute user's proc.        
        UserProc(UserParam);        
    }

    return 0;
}

void TestIOCompletePort(BOOL bWaitMode, LONG ThreadNum)
{
    HANDLE CompletePort;
    OVERLAPPED Overlapped = {0, 0, 0, 0, NULL};

    CompletePort = CreateIoCompletionPort(
        INVALID_HANDLE_VALUE,
        NULL,
        NULL,
        0);
    
    // Create threads.
    for(int i=0; i<ThreadNum; i++)
    {
        HANDLE hThread = CreateThread(NULL,
            0,
            IOCPWorkThread,
            CompletePort,
            0,
            NULL);

        CloseHandle(hThread);
    }


    CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    BeginTime = GetTickCount();
    ItemCount = 20;

    for(i=0; i<20; i++)
    {
        PostQueuedCompletionStatus(
            CompletePort,
            (DWORD)bWaitMode,
            (DWORD)UserProc1,
            &Overlapped);
    }
    
    WaitForSingleObject(CompleteEvent, INFINITE);
    CloseHandle(CompleteEvent);


    // Destroy all threads.
    for(i=0; i<ThreadNum; i++)
    {
        PostQueuedCompletionStatus(
            CompletePort,
            NULL,
            NULL,
            &Overlapped);
    }

    Sleep(1000); // wait all thread exit.

    CloseHandle(CompletePort);
}


參考書目

1,    MSDN Library 
2,    《Windows高級編程指南》
3,    《Windows核心編程》
4,    《Windows 2000 設備驅動程序設計指南》


異步IO、APC、IO完成端口、線程池與高性能服務器之四 線程池

線程池

下面摘抄于MSDN《Thread Pooling》。
有 許多應用程序創建的線程花費了大量時間在睡眠狀態來等待事件的發生。還有一些線程進入睡眠狀態后定期被喚醒以輪詢工作方式來改變或者更新狀態信息。線程池 可以讓你更有效地使用線程,它為你的應用程序提供一個由系統管理的工作者線程池。至少會有一個線程來監聽放到線程池的所有等待操作,當等待操作完成后,線 程池中將會有一個工作者線程來執行相應的回調函數。
你也可以把沒有等待操作的工作項目放到線程池中,用QueueUserWorkItem函數來完成這個工作,把要執行的工作項目函數通過一個參數傳遞給線程池。工作項目被放到線程池中后,就不能再取消了。
Timer-queue timers和Registered wait operations也使用線程池來實現。他們的回調函數也放在線程池中。你也可以用BindIOCompletionCallback函數來投遞一個異 步IO操作,在IO完成端口上,回調函數也是由線程池線程來執行。
當第一次調用QueueUserWorkItem函數或者 BindIOCompletionCallback函數的時候,線程池被自動創建,或者Timer-queue timers或者Registered wait operations放入回調函數的時候,線程池也可以被創建。線程池可以創建的線程數量不限,僅受限于可用的內存,每一個線程使用默認的初始堆棧大小, 運行在默認的優先級上。
線程池中有兩種類型的線程:IO線程和非IO線程。IO線程等待在可告警狀態,工作項目作為APC放到IO線程中。如果你的工作項目需要線程執行在可警告狀態,你應該將它放到IO線程。
非IO工作者線程等待在IO完成端口上,使用非IO線程比IO線程效率更高,也就是說,只要有可能的話,盡量使用非IO線程。IO線程和非IO線程在異步IO操作沒有完成之前都不會退出。然而,不要在非IO線程中發出需要很長時間才能完成的異步IO請求。
正 確使用線程池的方法是,工作項目函數以及它將會調用到的所有函數都必須是線程池安全的。安全的函數不應該假設線程是一次性線程的或者是永久線程。一般來 說,應該避免使用線程本地存儲和發出需要永久線程的異步IO調用,比如說RegNotifyChangeKeyValue函數。如果需要在永久線程中執行 這樣的函數的話,可以給QueueUserWorkItem傳遞一個選項WT_EXECUTEINPERSISTENTTHREAD。
注意,線程池不能兼容COM的單線程套間(STA)模型。

    為了更深入地講解操作系統實現的線程池的優越性,我們首先嘗試著自己實現一個簡單的線程池模型。

    代碼如下:

/************************************************************************/
/* Test Our own thread pool.                                            */
/************************************************************************/

typedef struct _THREAD_POOL
{
    HANDLE QuitEvent;
    HANDLE WorkItemSemaphore;

    LONG WorkItemCount;
    LIST_ENTRY WorkItemHeader;
    CRITICAL_SECTION WorkItemLock;

    LONG ThreadNum;
    HANDLE *ThreadsArray;

}THREAD_POOL, *PTHREAD_POOL;

typedef VOID (*WORK_ITEM_PROC)(PVOID Param);

typedef struct _WORK_ITEM
{
    LIST_ENTRY List;

    WORK_ITEM_PROC UserProc;
    PVOID UserParam;
    
}WORK_ITEM, *PWORK_ITEM;


DWORD WINAPI WorkerThread(PVOID pParam)
{
    PTHREAD_POOL pThreadPool = (PTHREAD_POOL)pParam;
    HANDLE Events[2];
    
    Events[0] = pThreadPool->QuitEvent;
    Events[1] = pThreadPool->WorkItemSemaphore;

    for(;;)
    {
        DWORD dwRet = WaitForMultipleObjects(2, Events, FALSE, INFINITE);

        if(dwRet == WAIT_OBJECT_0)
            break;

        //
        // execute user's proc.
        //

        else if(dwRet == WAIT_OBJECT_0 +1)
        {
            PWORK_ITEM pWorkItem;
            PLIST_ENTRY pList;

            EnterCriticalSection(&pThreadPool->WorkItemLock);
            _ASSERT(!IsListEmpty(&pThreadPool->WorkItemHeader));
            pList = RemoveHeadList(&pThreadPool->WorkItemHeader);
            LeaveCriticalSection(&pThreadPool->WorkItemLock);

            pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List);
            pWorkItem->UserProc(pWorkItem->UserParam);

            InterlockedDecrement(&pThreadPool->WorkItemCount);
            free(pWorkItem);
        }

        else
        {
            _ASSERT(0);
            break;
        }
    }

    return 0;
}

BOOL InitializeThreadPool(PTHREAD_POOL pThreadPool, LONG ThreadNum)
{
    pThreadPool->QuitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    pThreadPool->WorkItemSemaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
    pThreadPool->WorkItemCount = 0;
    InitializeListHead(&pThreadPool->WorkItemHeader);
    InitializeCriticalSection(&pThreadPool->WorkItemLock);
    pThreadPool->ThreadNum = ThreadNum;
    pThreadPool->ThreadsArray = (HANDLE*)malloc(sizeof(HANDLE) * ThreadNum);

    for(int i=0; i<ThreadNum; i++)
    {
        pThreadPool->ThreadsArray[i] = CreateThread(NULL, 0, WorkerThread, pThreadPool, 0, NULL);
    }

    return TRUE;
}

VOID DestroyThreadPool(PTHREAD_POOL pThreadPool)
{
    SetEvent(pThreadPool->QuitEvent);

    for(int i=0; i<pThreadPool->ThreadNum; i++)
    {
        WaitForSingleObject(pThreadPool->ThreadsArray[i], INFINITE);
        CloseHandle(pThreadPool->ThreadsArray[i]);
    }

    free(pThreadPool->ThreadsArray);

    CloseHandle(pThreadPool->QuitEvent);
    CloseHandle(pThreadPool->WorkItemSemaphore);
    DeleteCriticalSection(&pThreadPool->WorkItemLock);

    while(!IsListEmpty(&pThreadPool->WorkItemHeader))
    {
        PWORK_ITEM pWorkItem;
        PLIST_ENTRY pList;
        
        pList = RemoveHeadList(&pThreadPool->WorkItemHeader);
        pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List);
        
        free(pWorkItem);
    }
}

BOOL PostWorkItem(PTHREAD_POOL pThreadPool, WORK_ITEM_PROC UserProc, PVOID UserParam)
{
    PWORK_ITEM pWorkItem = (PWORK_ITEM)malloc(sizeof(WORK_ITEM));
    if(pWorkItem == NULL)
        return FALSE;

    pWorkItem->UserProc = UserProc;
    pWorkItem->UserParam = UserParam;

    EnterCriticalSection(&pThreadPool->WorkItemLock);
    InsertTailList(&pThreadPool->WorkItemHeader, &pWorkItem->List);
    LeaveCriticalSection(&pThreadPool->WorkItemLock);

    InterlockedIncrement(&pThreadPool->WorkItemCount);

    ReleaseSemaphore(pThreadPool->WorkItemSemaphore, 1, NULL);

    return TRUE;
}

VOID UserProc1(PVOID dwParam)
{
    WorkItem(dwParam);
}

void TestSimpleThreadPool(BOOL bWaitMode, LONG ThreadNum)
{
    THREAD_POOL ThreadPool;    
    InitializeThreadPool(&ThreadPool, ThreadNum);
    
    CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    BeginTime = GetTickCount();
    ItemCount = 20;

    for(int i=0; i<20; i++)
    {
        PostWorkItem(&ThreadPool, UserProc1, (PVOID)bWaitMode);
    }
    
    WaitForSingleObject(CompleteEvent, INFINITE);
    CloseHandle(CompleteEvent);

    DestroyThreadPool(&ThreadPool);
}
    我們把工作項目放到一個隊列中,用一個信號量通知線程池,線程池中任意一個線程取出工作項目來執行,執行完畢之后,線程返回線程池,繼續等待新的工作項目。
    線程池中線程的數量是固定的,預先創建好的,永久的線程,直到銷毀線程池的時候,這些線程才會被銷毀。
    線程池中線程獲得工作項目的機會是均等的,隨機的,并沒有特別的方式保證哪一個線程具有特殊的優先獲得工作項目的機會。
    而且,同一時刻可以并發運行的線程數目沒有任何限定。事實上,在我們的執行計算任務的演示代碼中,所有的線程都并發執行。
    下面,我們再來看一下,完成同樣的任務,系統提供的線程池是如何運作的。

/************************************************************************/
/* QueueWorkItem Test.                                                  */
/************************************************************************/

DWORD BeginTime;
LONG  ItemCount;
HANDLE CompleteEvent;

int compute()
{
    srand(BeginTime);

    for(int i=0; i<20 *1000 * 1000; i++)
        rand();

    return rand();
}

DWORD WINAPI WorkItem(LPVOID lpParameter)
{
    BOOL bWaitMode = (BOOL)lpParameter;

    if(bWaitMode)
        Sleep(1000);
    else
        compute();

    if(InterlockedDecrement(&ItemCount) == 0)
    {
        printf("Time total %d second.\n", GetTickCount() - BeginTime);
        SetEvent(CompleteEvent);
    }

    return 0;
}

void TestWorkItem(BOOL bWaitMode, DWORD Flag)
{
    CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    BeginTime = GetTickCount();
    ItemCount = 20;
    
    for(int i=0; i<20; i++)
    {
        QueueUserWorkItem(WorkItem, (PVOID)bWaitMode, Flag);
    }    

    WaitForSingleObject(CompleteEvent, INFINITE);
    CloseHandle(CompleteEvent);
}
    很簡單,是吧?我們僅需要關注于我們的回調函數即可。但是與我們的簡單模擬來比,系統提供的線程池有著更多的優點。
    首先,線程池中線程的數目是動態調整的,其次,線程池利用IO完成端口的特性,它可以限制并發運行的線程數目,默認情況下,將會限制為CPU的數目,這可以減少線程切換。它挑選最近執行過的線程再次投入執行,從而避免了不必要的線程切換。
    系統提供的線程池背后的策略,我們下一節繼續再談。

參考書目

1,    MSDN Library 
2,    《Windows高級編程指南》
3,    《Windows核心編程》
4,    《Windows 2000 設備驅動程序設計指南》


正文
異步IO、APC、IO完成端口、線程池與高性能服務器之五 服務器的性能指標與實現高性能的途徑

服務器的性能指標

    作為一個網絡服務器程序,性能永遠是第一位的指標。性能可以這樣定義:在給定的硬件條件和時間里,能夠處理的任務量。能夠最大限度地利用硬件性能的服務器設計才是良好的設計。
    設計良好的服務器還應該考慮平均服務,對于每一個客戶端,服務器應該給予每個客戶端平均的服務,不能讓某一個客戶端長時間得不到服務而發生“饑餓”的狀況。
    可伸縮性,也就是說,隨著硬件能力的提高,服務器的性能能夠隨之呈線性增長。

實現高性能的途徑

    一 個實際的服務器的計算是很復雜的,往往是混合了IO計算和CPU計算。IO計算指計算任務中以IO為主的計算模型,比如文件服務器、郵件服務器等,混合了 大量的網絡IO和文件IO;CPU計算指計算任務中沒有或很少有IO,比如加密/解密,編碼/解碼,數學計算等等。
    在CPU計算中,單線 程和多線程模型效果是相當的。《Win32多線程的性能》中說“在一個單處理器的計算機中,基于 CPU 的任務的并發執行速度不可能比串行執行速度快,但是我們可以看到,在 Windows NT 下線程創建和切換的額外開銷非常小;對于非常短的計算,并發執行僅僅比串行執行慢 10%,而隨著計算長度的增加,這兩個時間就非常接近了。”
    可見,對于純粹的CPU計算來說,如果只有一個CPU,多線程模型是不合適的。考慮一個執行密集的CPU計算的服務,如果有幾十個這樣的線程并發執行,過于頻繁地任務切換導致了不必要的性能損失。
    在 編程實現上,單線程模型計算模型對于服務器程序設計是很不方便的。因此,對于CPU計算采用線程池工作模型是比較恰當的。 QueueUserWorkItem函數非常適合于將一個CPU計算放入線程池。線程池實現將會努力減少這種不必要的線程切換,而且控制并發線程的數目為 CPU的數目。
    我們真正需要關心的是IO計算,一般的網絡服務器程序往往伴隨著大量的IO計算。提高性能的途徑在于要避免等待IO 的結束,造成CPU空閑,要盡量利用硬件能力,讓一個或多個IO設備與CPU并發執行。前面介紹的異步IO,APC,IO完成端口都可以達到這個目的。
    對 于網絡服務器來說,如果客戶端并發請求數目比較少的話,用簡單的多線程模型就可以應付了。如果一個線程因為等待IO操作完成而被掛起,操作系統將會調度另 外一個就緒的線程投入運行,從而形成并發執行。經典的網絡服務器邏輯大多采用多線程/多進程方式,在一個客戶端發起到服務器的連接時,服務器將會創建一個 線程,讓這個新的線程來處理后續事務。這種以一個專門的線程/進程來代表一個客戶端對象的編程方法非常直觀,易于理解。
    對于大型網絡服務 器程序來說,這種方式存在著局限性。首先,創建線程/進程和銷毀線程/進程的代價非常高昂,尤其是在服務器采用TCP“短連接”方式或UDP方式通訊的情 況下,例如,HTTP協議中,客戶端發起一個連接后,發送一個請求,服務器回應了這個請求后,連接也就被關閉了。如果采用經典方式設計HTTP服務器,那 么過于頻繁地創建線程/銷毀線程對性能造成的影響是很惡劣的。
    其次,即使一個協議中采取TCP“長連接”,客戶端連上服務器后就一直保持 此連接,經典的設計方式也是有弊病的。如果客戶端并發請求量很高,同一時刻有很多客戶端等待服務器響應的情況下,將會有過多的線程并發執行,頻繁的線程切 換將用掉一部分計算能力。實際上,如果并發線程數目過多的話,往往會過早地耗盡物理內存,絕大部分時間耗費在線程切換上,因為線程切換的同時也將引起內存 調頁。最終導致服務器性能急劇下降,
    對于一個需要應付同時有大量客戶端并發請求的網絡服務器來說,線程池是唯一的解決方案。線程池不光能夠避免頻繁地創建線程和銷毀線程,而且能夠用數目很少的線程就可以處理大量客戶端并發請求。
    值得注意的是,對于一個壓力不大的網絡服務器程序設計,我們并不推薦以上任何技巧。在簡單的設計就能夠完成任務的情況下,把事情弄得很復雜是很不明智,很愚蠢的行為。
from:
http://blog.chinaunix.net/u2/67780/showart_2057137.html
原文地址 http://blog.chinaunix.net/u/14774/showart_88161.html
posted on 2010-01-04 15:21 chatler 閱讀(763) 評論(0)  編輯 收藏 引用 所屬分類: windows
<2009年11月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

常用鏈接

留言簿(10)

隨筆分類(307)

隨筆檔案(297)

algorithm

Books_Free_Online

C++

database

Linux

Linux shell

linux socket

misce

  • cloudward
  • 感覺這個博客還是不錯,雖然做的東西和我不大相關,覺得看看還是有好處的

network

OSS

  • Google Android
  • Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This early look at the Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.
  • os161 file list

overall

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产精品亚洲综合一区在线观看| 久久久久久9999| 亚洲午夜一区二区三区| 亚洲最新色图| 欧美视频免费看| 欧美一级播放| 久久久久久久久蜜桃| 亚洲精品女人| 亚洲一区日韩在线| 国内精品99| 亚洲日本中文字幕免费在线不卡| 欧美日本三区| 久久久久成人精品| 免费在线视频一区| 亚洲欧美日本日韩| 久热国产精品| 亚洲一区欧美激情| 另类亚洲自拍| 亚洲欧美在线免费| 免费成人性网站| 性色av一区二区三区| 免费观看成人www动漫视频| 在线综合欧美| 久久久久久成人| 亚洲一区久久| 欧美高清视频在线观看| 欧美在线在线| 欧美片在线观看| 免费看精品久久片| 国产精品永久免费观看| 亚洲激情不卡| 亚洲大胆在线| 亚洲午夜国产一区99re久久| 亚洲黄色影院| 久久精品官网| 欧美诱惑福利视频| 欧美体内谢she精2性欧美| 女生裸体视频一区二区三区| 国产精品一区免费观看| 亚洲免费精彩视频| 亚洲日韩成人| 老司机成人在线视频| 久久精品国产一区二区电影| 欧美体内谢she精2性欧美| 亚洲国产日韩欧美在线动漫| 狠狠色丁香婷婷综合| 亚洲一区二区三区涩| 99视频精品全国免费| 免费在线欧美视频| 欧美99久久| 有坂深雪在线一区| 久久免费午夜影院| 久久综合国产精品| 韩国成人理伦片免费播放| 亚洲欧美中文在线视频| 午夜精品久久久久久久白皮肤| 欧美日韩免费网站| 日韩一级黄色片| 中文日韩在线视频| 国产精品国产三级国产 | 亚洲国产91精品在线观看| 欧美在线高清| 久久人91精品久久久久久不卡 | 欧美激情国产高清| av不卡在线| 亚洲午夜精品网| 亚洲欧美日韩区| 国产精品一区二区你懂的| 亚洲婷婷综合久久一本伊一区| 亚洲女与黑人做爰| 国产精品高清网站| 午夜视频在线观看一区二区| 久久精品人人做人人爽| 影音先锋日韩有码| 欧美成人一二三| 日韩视频在线永久播放| 午夜精品美女自拍福到在线| 国产女人aaa级久久久级| 久久爱www久久做| 免费国产一区二区| 亚洲理伦在线| 国产精品日本精品| 久久久久国色av免费观看性色| 蜜臀久久99精品久久久久久9| 亚洲精品一区二区三区不| 欧美日韩亚洲综合一区| 亚洲女人小视频在线观看| 免费成人在线观看视频| 99综合精品| 国产一区二区福利| 欧美aⅴ一区二区三区视频| 亚洲精一区二区三区| 久久国产精品久久国产精品 | 国产欧美日韩不卡免费| 久久综合亚州| 亚洲私人影吧| 欧美成人午夜77777| 亚洲一区三区电影在线观看| 红杏aⅴ成人免费视频| 欧美日本在线观看| 久久精品一区| 亚洲视频一区| 亚洲国产视频a| 久久久久国产精品人| 一级成人国产| 影音先锋国产精品| 国产精品国色综合久久| 你懂的国产精品| 香蕉av777xxx色综合一区| 91久久国产综合久久蜜月精品 | 亚洲欧美日韩国产另类专区| 亚洲第一精品影视| 久久精品亚洲国产奇米99| 亚洲先锋成人| 亚洲精品欧洲精品| 亚洲成人在线网| 国产午夜精品理论片a级大结局| 欧美精品久久一区| 久久亚洲精品一区二区| 午夜精品视频在线观看| 亚洲少妇中出一区| 亚洲精品午夜| 亚洲国产成人不卡| 欧美成人一区二免费视频软件| 欧美在线影院| 欧美在线www| 欧美一区精品| 午夜综合激情| 亚洲欧美偷拍卡通变态| 亚洲淫性视频| 亚洲视频在线观看三级| 日韩一级欧洲| 日韩午夜精品| 欧美成人久久| 亚洲最新合集| 一本色道久久综合| 亚洲精品一区二区三区婷婷月| 欧美jizzhd精品欧美巨大免费| 久久久久青草大香线综合精品| 久久9热精品视频| 久久精品二区三区| 久久一区二区视频| 欧美mv日韩mv国产网站| 欧美电影免费观看高清| 欧美成人亚洲成人日韩成人| 欧美黄色aaaa| 亚洲激情第一页| 一区二区三区鲁丝不卡| 亚洲视频专区在线| 欧美在线亚洲在线| 久久理论片午夜琪琪电影网| 蜜臀久久99精品久久久久久9| 农夫在线精品视频免费观看| 欧美紧缚bdsm在线视频| 欧美人与性动交cc0o| 国产精品a级| 国产午夜精品全部视频播放| 在线精品视频一区二区三四| 亚洲国产精品悠悠久久琪琪| 亚洲乱码久久| 欧美亚洲在线| 美女视频一区免费观看| 亚洲人成小说网站色在线| 亚洲视频一二区| 久久久久久噜噜噜久久久精品 | 久久福利影视| 欧美69视频| 国产伦精品免费视频| 黄色小说综合网站| 一本色道久久综合狠狠躁的推荐| 亚洲欧美国产高清| 另类激情亚洲| 一区二区三区精密机械公司| 久久国产精品久久久久久| 欧美国产日韩二区| 国产日韩欧美日韩| 亚洲乱亚洲高清| 久久久久久久综合| 亚洲最新视频在线| 另类亚洲自拍| 国产区亚洲区欧美区| 一区二区三区**美女毛片 | 亚洲一区二区三区四区视频| 久久一区中文字幕| 一区二区三区高清| 美女福利精品视频| 国产婷婷97碰碰久久人人蜜臀| aaa亚洲精品一二三区| 久久久久久久综合日本| 亚洲最快最全在线视频| 免费影视亚洲| 一区二区三区在线观看视频 | 在线观看欧美黄色| 午夜精品在线| 99riav国产精品| 免费在线欧美视频| 在线视频国内自拍亚洲视频| 欧美一区二区三区另类 | 亚洲欧美日韩一区| 国产精品二区影院|