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

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

背景:輪詢 PIO DMA 中斷

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

同步文件IO和異步文件IO

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

參考書目

1,    MSDN Library 
2,    《Windows高級編程指南》
3,    《Windows核心編程》
4,    《Windows 2000 設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)指南》

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

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


參考書目

1,    MSDN Library 
2,    《Windows高級編程指南》
3,    《Windows核心編程》
4,    《Windows 2000 設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)指南》


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

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

/************************************************************************/
/* 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 設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)指南》


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

線程池

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

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

    代碼如下:

/************************************************************************/
/* 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);
}
    我們把工作項(xiàng)目放到一個(gè)隊(duì)列中,用一個(gè)信號量通知線程池,線程池中任意一個(gè)線程取出工作項(xiàng)目來執(zhí)行,執(zhí)行完畢之后,線程返回線程池,繼續(xù)等待新的工作項(xiàng)目。
    線程池中線程的數(shù)量是固定的,預(yù)先創(chuàng)建好的,永久的線程,直到銷毀線程池的時(shí)候,這些線程才會(huì)被銷毀。
    線程池中線程獲得工作項(xiàng)目的機(jī)會(huì)是均等的,隨機(jī)的,并沒有特別的方式保證哪一個(gè)線程具有特殊的優(yōu)先獲得工作項(xiàng)目的機(jī)會(huì)。
    而且,同一時(shí)刻可以并發(fā)運(yùn)行的線程數(shù)目沒有任何限定。事實(shí)上,在我們的執(zhí)行計(jì)算任務(wù)的演示代碼中,所有的線程都并發(fā)執(zhí)行。
    下面,我們再來看一下,完成同樣的任務(wù),系統(tǒng)提供的線程池是如何運(yù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);
}
    很簡單,是吧?我們僅需要關(guān)注于我們的回調(diào)函數(shù)即可。但是與我們的簡單模擬來比,系統(tǒng)提供的線程池有著更多的優(yōu)點(diǎn)。
    首先,線程池中線程的數(shù)目是動(dòng)態(tài)調(diào)整的,其次,線程池利用IO完成端口的特性,它可以限制并發(fā)運(yùn)行的線程數(shù)目,默認(rèn)情況下,將會(huì)限制為CPU的數(shù)目,這可以減少線程切換。它挑選最近執(zhí)行過的線程再次投入執(zhí)行,從而避免了不必要的線程切換。
    系統(tǒng)提供的線程池背后的策略,我們下一節(jié)繼續(xù)再談。

參考書目

1,    MSDN Library 
2,    《Windows高級編程指南》
3,    《Windows核心編程》
4,    《Windows 2000 設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)指南》


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

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

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

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

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

常用鏈接

留言簿(10)

隨筆分類(307)

隨筆檔案(297)

algorithm

Books_Free_Online

C++

database

Linux

Linux shell

linux socket

misce

  • cloudward
  • 感覺這個(gè)博客還是不錯(cuò),雖然做的東西和我不大相關(guān),覺得看看還是有好處的

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>
            国产农村妇女毛片精品久久麻豆 | 国产欧美一区二区精品婷婷| 欧美中文在线字幕| 亚洲人成精品久久久久| 欧美在线免费| 日韩一二三区视频| 在线观看欧美激情| 国产精品日日摸夜夜添夜夜av| 欧美jizzhd精品欧美巨大免费| 亚洲欧美国产va在线影院| 亚洲国产综合视频在线观看 | 亚洲大片在线| 久久精品国产一区二区三区免费看| aa日韩免费精品视频一| 一区二区在线观看视频| 国产精品一级二级三级| 欧美日韩国产精品一区二区亚洲| 老**午夜毛片一区二区三区| 亚洲男人的天堂在线观看| 日韩视频三区| 亚洲国产欧美一区二区三区同亚洲| 久久精品官网| 翔田千里一区二区| 妖精成人www高清在线观看| 亚洲精品日日夜夜| 亚洲高清三级视频| 极品少妇一区二区三区精品视频| 国产一区二区三区视频在线观看| 国产九九精品| 国产精品一二三| 国产精品美女久久久久久免费| 欧美偷拍一区二区| 国产精品久久久久一区二区三区共| 欧美日韩一级大片网址| 欧美三级韩国三级日本三斤| 欧美精品三级在线观看| 欧美激情在线播放| 欧美日韩蜜桃| 国产精品美女久久| 国产精品中文字幕欧美| 国产在线精品自拍| 伊人久久大香线蕉综合热线| 亚洲国产一区二区三区在线播 | 99精品国产在热久久| 99re热这里只有精品免费视频| 日韩手机在线导航| 亚洲视频一二三| 午夜日韩在线| 久久久久久久91| 欧美sm视频| 欧美日韩精品久久久| 国产精品免费视频xxxx| 国产日韩欧美高清| 在线精品一区| 日韩亚洲视频| 西瓜成人精品人成网站| 久久久久在线| 亚洲国产精品久久久久秋霞不卡| 亚洲精选在线观看| 亚洲综合导航| 久久嫩草精品久久久精品一| 欧美日韩成人一区二区| 国产精品尤物福利片在线观看| 精品91在线| 一区二区三区国产| 久久精精品视频| 亚洲第一区在线观看| 一区二区三区四区五区精品视频| 欧美亚洲免费在线| 欧美精品18+| 国产人成精品一区二区三| 亚洲高清在线| 亚洲伊人观看| 欧美成人激情在线| 亚洲性图久久| 欧美成人免费在线视频| 国产精品综合| 99re这里只有精品6| 久久久久国产免费免费| 亚洲啪啪91| 久久国产精品一区二区三区四区| 欧美精品在线一区| 樱桃国产成人精品视频| 亚洲性视频网站| 欧美成人免费在线观看| 亚洲综合第一页| 欧美理论视频| 伊人久久大香线蕉综合热线| 亚洲一区二区三区四区视频| 欧美1区2区视频| 亚洲欧美高清| 欧美日韩免费一区| 亚洲电影av| 久久精品国产久精国产思思| 日韩视频不卡中文| 美脚丝袜一区二区三区在线观看| 国产麻豆精品在线观看| 国产精品99久久久久久www| 免费观看亚洲视频大全| 亚洲女性喷水在线观看一区| 欧美日本韩国| 亚洲欧洲三级| 另类激情亚洲| 午夜在线视频观看日韩17c| 欧美视频不卡| 一本色道久久88精品综合| 欧美成人精品高清在线播放| 午夜在线精品| 国产伦精品一区二区三区| 亚洲视频电影图片偷拍一区| 亚洲国产婷婷| 蜜臀久久99精品久久久久久9 | 欧美成人在线影院| 久久久久国产精品一区| 亚洲欧美大片| 亚洲自拍偷拍网址| 久久久免费av| 国内精品久久久久久久97牛牛| 久久综合九色| 欧美不卡三区| 亚洲视频一区二区在线观看 | 黄色av日韩| 亚洲高清电影| 欧美日韩一区二区三区四区五区 | 亚洲视频一二| 国产一区清纯| 欧美福利视频在线| 欧美日韩国产色站一区二区三区| 亚洲一区二区三区在线播放| 翔田千里一区二区| 亚洲激情国产精品| 99视频一区二区| 国语自产在线不卡| 亚洲精品久久久久久久久久久久久 | 久久国产精品久久国产精品| 久久久久久夜| 中文精品视频一区二区在线观看| 午夜日韩福利| 亚洲狼人精品一区二区三区| 亚洲永久免费观看| 91久久精品网| 亚洲欧美激情四射在线日| 最新国产成人在线观看| 亚洲一区二区在线播放| 亚洲第一精品影视| 亚洲性感美女99在线| 亚洲国产裸拍裸体视频在线观看乱了| 亚洲免费精品| 伊人成综合网伊人222| 在线午夜精品自拍| 亚洲国产精品尤物yw在线观看 | 亚洲精品一区中文| 欧美一级欧美一级在线播放| 日韩系列欧美系列| 久久精品国产欧美激情| 亚洲天堂av综合网| 老**午夜毛片一区二区三区| 午夜视频久久久久久| 欧美国产日韩精品免费观看| 久久精品综合网| 欧美区在线播放| 美女主播一区| 国产伦理一区| 夜夜嗨av一区二区三区网页| 亚洲国产99精品国自产| 亚洲欧美卡通另类91av| 一区二区三区色| 美日韩精品视频| 久久蜜桃香蕉精品一区二区三区| 欧美日韩一区二区三区视频| 欧美激情亚洲一区| 国外成人性视频| 亚洲欧美国产高清| 亚洲一区二区四区| 欧美久久一区| 欧美大片一区| 伊甸园精品99久久久久久| 午夜国产不卡在线观看视频| 亚洲午夜视频| 欧美日韩国产在线播放网站| 欧美顶级艳妇交换群宴| 国产综合色在线| 午夜欧美精品久久久久久久| 午夜精品国产更新| 国产精品啊v在线| 亚洲美女在线一区| 日韩特黄影片| 欧美成人精品福利| 欧美激情欧美激情在线五月| 一区二区在线免费观看| 欧美一区二区三区四区在线| 欧美一区二区久久久| 国产精品美女999| 亚洲特级毛片| 亚洲欧美另类久久久精品2019| 欧美日韩一区自拍| aa日韩免费精品视频一| 亚洲视频电影在线| 国产精品成人一区二区三区吃奶| 日韩亚洲视频在线|