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

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>
            久久综合国产精品台湾中文娱乐网| 欧美一级二区| 欧美日韩少妇| 欧美另类在线播放| 欧美日韩网站| 欧美小视频在线观看| 国产精品中文字幕在线观看| 国产欧美日韩免费| 国产一区二区三区在线观看免费视频| 国产日韩欧美一区二区三区在线观看| 国产日韩精品在线观看| 1769国内精品视频在线播放| 一区三区视频| 中国女人久久久| 欧美一区免费视频| 欧美黄色免费网站| 正在播放日韩| 久久久久久久一区二区三区| 久久中文精品| 欧美日韩在线免费观看| 国产精品久久网站| 亚洲国产成人porn| 亚洲欧美精品在线| 免费久久精品视频| 国产乱码精品一区二区三区不卡 | 国产精品r级在线| 国产欧美日韩免费| 亚洲精品一品区二品区三品区| 亚洲无毛电影| 欧美成人资源网| 香港成人在线视频| 欧美日韩成人在线播放| 国产拍揄自揄精品视频麻豆| 亚洲精品美女久久久久| 午夜久久久久久| 99riav久久精品riav| 久久久在线视频| 国产欧美日韩综合一区在线播放 | 久久精品成人一区二区三区 | 欧美国产日韩在线观看| 亚洲欧洲99久久| 欧美三级在线| 亚洲美女色禁图| 久久综合给合| 亚洲欧美日本视频在线观看| 欧美日韩一区二区三区在线 | 欧美日韩一区二区三区在线 | 亚洲欧美福利一区二区| 欧美日韩国产在线观看| 亚洲国产精品久久久久婷婷884 | 午夜欧美大尺度福利影院在线看| 亚洲国产精品一区制服丝袜 | 亚洲国产一二三| 久久亚洲影院| 在线播放中文一区| 久久一区二区精品| 久久久久国产免费免费| 国产亚洲成精品久久| 午夜精品99久久免费| 亚洲午夜性刺激影院| 欧美四级在线| 欧美国产日韩精品| 美女日韩欧美| 精品二区视频| 榴莲视频成人在线观看| 欧美怡红院视频| 国产一区二区高清视频| 久久国产精品久久精品国产| 亚洲欧美偷拍卡通变态| 国产亚洲精品bt天堂精选| 欧美在线视频观看| 欧美在线一二三| 国产一区二区三区黄视频| 久久久青草婷婷精品综合日韩| 午夜日韩av| 在线成人激情| 亚洲国产成人av在线| 欧美日韩久久精品| 久久精品国产第一区二区三区最新章节 | 欧美久久久久免费| 99国产精品视频免费观看一公开| 亚洲人久久久| 国产女主播在线一区二区| 久久综合色8888| 欧美了一区在线观看| 香蕉久久夜色精品国产使用方法| 欧美一区二区免费视频| 亚洲欧洲日韩综合二区| 亚洲欧美日韩国产一区| 亚洲日韩欧美一区二区在线| 一本一本a久久| 国产综合欧美在线看| 亚洲人被黑人高潮完整版| 国产欧美精品在线观看| 欧美成人国产一区二区| 国产精品v片在线观看不卡| 久久人人爽人人| 欧美伦理91i| 免费成人高清| 国产欧美韩国高清| 亚洲理论在线| 亚洲国产日韩欧美在线动漫 | 老鸭窝91久久精品色噜噜导演| 一区二区三区成人 | 欧美午夜久久| 欧美xx69| 国产日产亚洲精品| 亚洲精品久久| 精品成人在线| 欧美一级在线亚洲天堂| 亚洲美女性视频| 久久精品国产亚洲一区二区| 亚洲免费一级电影| 欧美高清hd18日本| 久久视频国产精品免费视频在线| 欧美日韩免费| 亚洲国产黄色| 91久久久在线| 久久免费高清| 久久综合色影院| 国产婷婷97碰碰久久人人蜜臀| 老司机午夜精品| 亚洲一区国产一区| 欧美91福利在线观看| 久久se精品一区二区| 欧美午夜宅男影院在线观看| 亚洲第一黄网| 亚洲国产一区二区三区高清| 久久激五月天综合精品| 久久精品国产精品亚洲| 国产精品久久久久久久久久直播| 亚洲成人在线视频播放 | 亚洲视频二区| 亚洲天堂网在线观看| 欧美精品九九| 亚洲欧洲一区二区在线观看| 亚洲精品久久嫩草网站秘色 | 久久久精品2019中文字幕神马| 国产精品欧美一区喷水| 亚洲亚洲精品三区日韩精品在线视频| 一区二区高清视频| 欧美婷婷在线| 亚洲午夜激情免费视频| 欧美一区在线看| 狠狠色丁香久久综合频道| 午夜一区在线| 免费看黄裸体一级大秀欧美| 激情五月综合色婷婷一区二区| 久久久女女女女999久久| 免费成人在线观看视频| 亚洲精品久久久蜜桃| 欧美视频一区在线观看| 这里只有精品在线播放| 欧美一区二区三区电影在线观看| 国产小视频国产精品| 久久综合伊人77777| 亚洲人成网站精品片在线观看| 中文亚洲字幕| 国产日韩欧美综合| 久久久天天操| 一区二区国产精品| 久久夜色精品国产噜噜av| 亚洲精品一区二区三区99| 欧美视频一区二区三区在线观看| 篠田优中文在线播放第一区| 欧美激情久久久久| 欧美一区二区免费视频| 亚洲激情精品| 国产精品视频免费在线观看| 久久不射电影网| 一本色道88久久加勒比精品| 欧美专区在线| 在线一区欧美| 亚洲区免费影片| 很黄很黄激情成人| 国产精品美女999| 免费在线欧美黄色| 欧美一区二区三区视频免费| 99pao成人国产永久免费视频| 久久精品国产2020观看福利| 一本一本久久a久久精品综合妖精| 国产一区二区激情| 欧美午夜不卡视频| 欧美激情一二三区| 久热精品视频在线观看| 午夜激情综合网| 国产精品色婷婷久久58| 你懂的国产精品| 亚洲欧美日韩在线观看a三区 | 亚洲一区二区av电影| 激情欧美日韩一区| 国产精品日本精品| 欧美日韩国产区一| 久久伊人亚洲| 欧美中在线观看| 亚洲欧美中文在线视频| 亚洲一二三四区| 亚洲婷婷免费| 99在线热播精品免费| 欧美激情a∨在线视频播放|