欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
異步IO、APC、IO完成端口、線(xiàn)程池與高性能服務(wù)器

異步IO、APC、IO完成端口、線(xiàn)程池與高性能服務(wù)器

                                      

原作者姓名 Fang(fangguicheng@21cn.com)

異步IO、APC、IO完成端口、線(xiàn)程池與高性能服務(wù)器之一 異步IO

背景:輪詢(xún) PIO DMA 中斷

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

同步文件IO和異步文件IO

下面摘抄于MSDN《synchronous file I/O and asynchronous file I/O》。
有兩種類(lèi)型的文件IO同步:同步文件IO和異步文件IO。異步文件IO也就是重疊IO。
在同步文件IO中,線(xiàn)程啟動(dòng)一個(gè)IO操作然后就立即進(jìn)入等待狀態(tài),直到IO操作完成后才醒來(lái)繼續執行。而異步文件IO方式中,線(xiàn)程發(fā)送一個(gè)IO請求到內核,然后繼續處理其他的事情,內核完成IO請求后,將會(huì )通知線(xiàn)程IO操作完成了。


    如果IO請求需要大量時(shí)間執行的話(huà),異步文件IO方式可以顯著(zhù)提高效率,因為在線(xiàn)程等待的這段時(shí)間內,CPU將會(huì )調度其他線(xiàn)程進(jìn)行執行,如果沒(méi)有其他線(xiàn)程需要執行的話(huà),這段時(shí)間將會(huì )浪費掉(可能會(huì )調度操作系統的零頁(yè)線(xiàn)程)。如果IO請求操作很快,用異步IO方式反而還低效,還不如用同步IO方式。
    同步IO在同一時(shí)刻只允許一個(gè)IO操作,也就是說(shuō)對于同一個(gè)文件句柄的IO操作是序列化的,即使使用兩個(gè)線(xiàn)程也不能同時(shí)對同一個(gè)文件句柄同時(shí)發(fā)出讀寫(xiě)操作。重疊IO允許一個(gè)或多個(gè)線(xiàn)程同時(shí)發(fā)出IO請求。
    異步IO在請求完成時(shí),通過(guò)將文件句柄設為有信號狀態(tài)來(lái)通知應用程序,或者應用程序通過(guò)GetOverlappedResult察看IO請求是否完成,也可以通過(guò)一個(gè)事件對象來(lái)通知應用程序。

參考書(shū)目

1,    MSDN Library
2,    《Windows高級編程指南》
3,    《Windows核心編程》
4,    《Windows 2000 設備驅動(dòng)程序設計指南》
異步IO、APC、IO完成端口、線(xiàn)程池與高性能服務(wù)器之二 APC

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

異步IO、APC、IO完成端口、線(xiàn)程池與高性能服務(wù)器之三 IO完成端口

IO完成端口

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

下面的代碼利用IO完成端口做了一個(gè)簡(jiǎn)單的線(xiàn)程池。

/************************************************************************/
/* 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    {
        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    {
        PostQueuedCompletionStatus(
            CompletePort,
            NULL,
            NULL,
            &Overlapped);
    }

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

    CloseHandle(CompletePort);
}

異步IO、APC、IO完成端口、線(xiàn)程池與高性能服務(wù)器之四 線(xiàn)程池

線(xiàn)程池

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

    為了更深入地講解操作系統實(shí)現的線(xiàn)程池的優(yōu)越性,我們首先嘗試著(zhù)自己實(shí)現一個(gè)簡(jiǎn)單的線(xiàn)程池模型。

    代碼如下:

/************************************************************************/
/* 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    {
        pThreadPool->ThreadsArray[i] = CreateThread(NULL, 0, WorkerThread, pThreadPool, 0, NULL);
    }

    return TRUE;
}

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

    for(int i=0; iThreadNum; 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);
}
    我們把工作項目放到一個(gè)隊列中,用一個(gè)信號量通知線(xiàn)程池,線(xiàn)程池中任意一個(gè)線(xiàn)程取出工作項目來(lái)執行,執行完畢之后,線(xiàn)程返回線(xiàn)程池,繼續等待新的工作項目。
    線(xiàn)程池中線(xiàn)程的數量是固定的,預先創(chuàng )建好的,永久的線(xiàn)程,直到銷(xiāo)毀線(xiàn)程池的時(shí)候,這些線(xiàn)程才會(huì )被銷(xiāo)毀。
    線(xiàn)程池中線(xiàn)程獲得工作項目的機會(huì )是均等的,隨機的,并沒(méi)有特別的方式保證哪一個(gè)線(xiàn)程具有特殊的優(yōu)先獲得工作項目的機會(huì )。
    而且,同一時(shí)刻可以并發(fā)運行的線(xiàn)程數目沒(méi)有任何限定。事實(shí)上,在我們的執行計算任務(wù)的演示代碼中,所有的線(xiàn)程都并發(fā)執行。
    下面,我們再來(lái)看一下,完成同樣的任務(wù),系統提供的線(xiàn)程池是如何運作的。

/************************************************************************/
/* 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);
}
    很簡(jiǎn)單,是吧?我們僅需要關(guān)注于我們的回調函數即可。但是與我們的簡(jiǎn)單模擬來(lái)比,系統提供的線(xiàn)程池有著(zhù)更多的優(yōu)點(diǎn)。
    首先,線(xiàn)程池中線(xiàn)程的數目是動(dòng)態(tài)調整的,其次,線(xiàn)程池利用IO完成端口的特性,它可以限制并發(fā)運行的線(xiàn)程數目,默認情況下,將會(huì )限制為CPU的數目,這可以減少線(xiàn)程切換。它挑選最近執行過(guò)的線(xiàn)程再次投入執行,從而避免了不必要的線(xiàn)程切換。
    系統提供的線(xiàn)程池背后的策略,我們下一節繼續再談。

異步IO、APC、IO完成端口、線(xiàn)程池與高性能服務(wù)器之五 服務(wù)器的性能指標與實(shí)現高性能的途徑

服務(wù)器的性能指標

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

實(shí)現高性能的途徑

    一個(gè)實(shí)際的服務(wù)器的計算是很復雜的,往往是混合了IO計算和CPU計算。IO計算指計算任務(wù)中以IO為主的計算模型,比如文件服務(wù)器、郵件服務(wù)器等,混合了大量的網(wǎng)絡(luò )IO和文件IO;CPU計算指計算任務(wù)中沒(méi)有或很少有IO,比如加密/解密,編碼/解碼,數學(xué)計算等等。
    在CPU計算中,單線(xiàn)程和多線(xiàn)程模型效果是相當的?!禬in32多線(xiàn)程的性能》中說(shuō)“在一個(gè)單處理器的計算機中,基于 CPU 的任務(wù)的并發(fā)執行速度不可能比串行執行速度快,但是我們可以看到,在 Windows NT 下線(xiàn)程創(chuàng )建和切換的額外開(kāi)銷(xiāo)非常??;對于非常短的計算,并發(fā)執行僅僅比串行執行慢 10%,而隨著(zhù)計算長(cháng)度的增加,這兩個(gè)時(shí)間就非常接近了。”
    可見(jiàn),對于純粹的CPU計算來(lái)說(shuō),如果只有一個(gè)CPU,多線(xiàn)程模型是不合適的??紤]一個(gè)執行密集的CPU計算的服務(wù),如果有幾十個(gè)這樣的線(xiàn)程并發(fā)執行,過(guò)于頻繁地任務(wù)切換導致了不必要的性能損失。
    在編程實(shí)現上,單線(xiàn)程模型計算模型對于服務(wù)器程序設計是很不方便的。因此,對于CPU計算采用線(xiàn)程池工作模型是比較恰當的。QueueUserWorkItem函數非常適合于將一個(gè)CPU計算放入線(xiàn)程池。線(xiàn)程池實(shí)現將會(huì )努力減少這種不必要的線(xiàn)程切換,而且控制并發(fā)線(xiàn)程的數目為CPU的數目。
    我們真正需要關(guān)心的是IO計算,一般的網(wǎng)絡(luò )服務(wù)器程序往往伴隨著(zhù)大量的IO計算。提高性能的途徑在于要避免等待IO 的結束,造成CPU空閑,要盡量利用硬件能力,讓一個(gè)或多個(gè)IO設備與CPU并發(fā)執行。前面介紹的異步IO,APC,IO完成端口都可以達到這個(gè)目的。
    對于網(wǎng)絡(luò )服務(wù)器來(lái)說(shuō),如果客戶(hù)端并發(fā)請求數目比較少的話(huà),用簡(jiǎn)單的多線(xiàn)程模型就可以應付了。如果一個(gè)線(xiàn)程因為等待IO操作完成而被掛起,操作系統將會(huì )調度另外一個(gè)就緒的線(xiàn)程投入運行,從而形成并發(fā)執行。經(jīng)典的網(wǎng)絡(luò )服務(wù)器邏輯大多采用多線(xiàn)程/多進(jìn)程方式,在一個(gè)客戶(hù)端發(fā)起到服務(wù)器的連接時(shí),服務(wù)器將會(huì )創(chuàng )建一個(gè)線(xiàn)程,讓這個(gè)新的線(xiàn)程來(lái)處理后續事務(wù)。這種以一個(gè)專(zhuān)門(mén)的線(xiàn)程/進(jìn)程來(lái)代表一個(gè)客戶(hù)端對象的編程方法非常直觀(guān),易于理解。
    對于大型網(wǎng)絡(luò )服務(wù)器程序來(lái)說(shuō),這種方式存在著(zhù)局限性。首先,創(chuàng )建線(xiàn)程/進(jìn)程和銷(xiāo)毀線(xiàn)程/進(jìn)程的代價(jià)非常高昂,尤其是在服務(wù)器采用TCP“短連接”方式或UDP方式通訊的情況下,例如,HTTP協(xié)議中,客戶(hù)端發(fā)起一個(gè)連接后,發(fā)送一個(gè)請求,服務(wù)器回應了這個(gè)請求后,連接也就被關(guān)閉了。如果采用經(jīng)典方式設計HTTP服務(wù)器,那么過(guò)于頻繁地創(chuàng )建線(xiàn)程/銷(xiāo)毀線(xiàn)程對性能造成的影響是很惡劣的。
    其次,即使一個(gè)協(xié)議中采取TCP“長(cháng)連接”,客戶(hù)端連上服務(wù)器后就一直保持此連接,經(jīng)典的設計方式也是有弊病的。如果客戶(hù)端并發(fā)請求量很高,同一時(shí)刻有很多客戶(hù)端等待服務(wù)器響應的情況下,將會(huì )有過(guò)多的線(xiàn)程并發(fā)執行,頻繁的線(xiàn)程切換將用掉一部分計算能力。實(shí)際上,如果并發(fā)線(xiàn)程數目過(guò)多的話(huà),往往會(huì )過(guò)早地耗盡物理內存,絕大部分時(shí)間耗費在線(xiàn)程切換上,因為線(xiàn)程切換的同時(shí)也將引起內存調頁(yè)。最終導致服務(wù)器性能急劇下降,
    對于一個(gè)需要應付同時(shí)有大量客戶(hù)端并發(fā)請求的網(wǎng)絡(luò )服務(wù)器來(lái)說(shuō),線(xiàn)程池是唯一的解決方案。線(xiàn)程池不光能夠避免頻繁地創(chuàng )建線(xiàn)程和銷(xiāo)毀線(xiàn)程,而且能夠用數目很少的線(xiàn)程就可以處理大量客戶(hù)端并發(fā)請求。
    值得注意的是,對于一個(gè)壓力不大的網(wǎng)絡(luò )服務(wù)器程序設計,我們并不推薦以上任何技巧。在簡(jiǎn)單的設計就能夠完成任務(wù)的情況下,把事情弄得很復雜是很不明智,很愚蠢的行為。

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Windows系統編程之異步I/O和完成端口
完成端口詳解
《Windows核心編程系列》十談?wù)????同步設備IO與異步設備IO之異步IO
I/O管理器執行IO完成
惡意代碼常用技術(shù)解析
【轉】NT與Unix系統架構圖
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久