基於多線程的EventSelect

單線程的: 異步io通知 WSAEventSelect

單線程上最多隻能接受64個event;

改版多線程的 , 大概思路:

1. 主線程就接受一個監聽套接字, accept返回的socket 仍到全局隊列中

2. 一個分配socket的線程 用於控制 socket 分配到哪個 監聽事件的線程中去 [ dispath thread] ;

3. 創建一個線程池 默認4個線程, 每個線程分別監聽  WSA_MAXIMUM_WAIT_EVENTS - 1 個事件, 第一個事件用於分派線程通知

有新的socket 來了 ; 這樣 4個線程總共可監聽 (WSA_MAXIMUM_WAIT_EVENTS - 1 ) * 4 個socket;

代碼有點亂, 也沒測試過; 純粹一種思路

用了大量的線程同步,其實不太好 , 可優化很多地方;

#include <iostream>
#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include <process.h>
#include <deque>
#include <vector>
#include <unordered_map>
#pragma comment(lib, "ws2_32.lib")
#define PORT 9988
using namespace std;


//socket對象
typedef struct _SOCK_OBJ
{
    enum {BUFFSIZE = 4096};
    SOCKET sock;
    char *buf;
    _SOCK_OBJ(SOCKET s):sock(s), buf(new char[BUFFSIZE]) {}
    ~_SOCK_OBJ(){
        if(sock != INVALID_SOCKET)
            closesocket(sock);
        if(buf){
            delete [] buf;
        }
    }
}SOCK_OBJ,*PSOCK_OBJ;


//每個線程一個
typedef struct _THREAD_INFO{
    WSAEVENT * event_arr; //所有的事件都在這
    HANDLE thread_handle; //線程handler
    vector<SOCK_OBJ*> sock_obj_arr; //socket對象
    char nCount; // 當前數量
    SRWLOCK lock; //讀寫鎖

    _THREAD_INFO():nCount(0),event_arr(0){
       sock_obj_arr.reserve(WSA_MAXIMUM_WAIT_EVENTS/2);
       event_arr = new WSAEVENT[WSA_MAXIMUM_WAIT_EVENTS];
       event_arr[0] = WSACreateEvent();
       InitializeSRWLock(&lock);
    }

    //沒用到 
    int get_sock_count() {
        int i = 0;
        AcquireSRWLockShared(&lock);
        i  = nCount;
        ReleaseSRWLockShared(&lock);
        return i;
    }
// 觸發一次添加操作, 有socket 來了
    void awake(){
        WSASetEvent(event_arr[0]);
    }

   //是否已經滿了
    bool aviliable(){
        bool ret = false;
       AcquireSRWLockShared(&lock);
        if( nCount < WSA_MAXIMUM_WAIT_EVENTS - 1)
            ret = true;
       ReleaseSRWLockShared(&lock);
        return ret;
    }
    ~_THREAD_INFO(){
        if(event_arr){
            WSACloseEvent(event_arr[0]);
        }
        for(int i =0 ; i < nCount; ++i){
            delete sock_obj_arr[i];
            WSACloseEvent(event_arr[i+1]);
        }
    }

}THREAD_INFO,*PTHREAD_INFO;

CRITICAL_SECTION cs_queue;  // 全局鎖, accept的socket放入gqueue
CONDITION_VARIABLE cond_queue; //同上, 用於線程同步
volatile bool bEnd = false;
deque<SOCKET> gqueue; //socket隊列


deque<SOCK_OBJ*> sock_queue; //socket對象隊列 
SRWLOCK glock; //鎖, 用於工作線程與 dispath thread
unordered_map<DWORD , THREAD_INFO*> gmap; //線程id , 線程信息對象


//重新排一下event_arr 
void rebulid(THREAD_INFO * pinfo , int idx , int total){
    for(int i  = idx; i < total ; ++i)
        pinfo->event_arr[i] = pinfo->event_arr[i+1];
}

//工作線程
unsigned int __stdcall workthread(void *args){
    printf("workthread %ld\n" , GetCurrentThreadId());
    THREAD_INFO * pinfo = (THREAD_INFO*)args;
    HANDLE t ;
    DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),&t,0,FALSE,DUPLICATE_SAME_ACCESS);
    pinfo->thread_handle = t;
    int pos = 0 , startindex= 0 ,nindex = 0,len = 0,nevents = 0;
    WSANETWORKEVENTS net_ev;
    while(1){
        nevents = pinfo->nCount+1;
        pos = WSAWaitForMultipleEvents(nevents,pinfo->event_arr,FALSE,WSA_INFINITE,FALSE);
        startindex = pos - WSA_WAIT_EVENT_0;
        for(int i = startindex ; i < nevents; ++i){
            nindex = WSAWaitForMultipleEvents(1,pinfo->event_arr+i,TRUE,0,FALSE);
            if(WSA_WAIT_FAILED == nindex || WSA_WAIT_TIMEOUT == nindex){
                continue;
            }
            else if(0 == i){

                // 有新的socket 添加, 加入event_arr 與 socket_arr
                SOCK_OBJ * psock = NULL;
                AcquireSRWLockExclusive(&glock);
                psock = sock_queue.front();
                sock_queue.pop_front();
                ReleaseSRWLockExclusive(&glock);

                if(pinfo->nCount < WSA_MAXIMUM_WAIT_EVENTS -1){
                    pinfo->sock_obj_arr.push_back(psock);
                    WSAEVENT ev = WSACreateEvent();
                    pinfo->event_arr[pinfo->nCount] =ev ;
                    WSAEventSelect(psock->sock,ev, FD_READ|FD_CLOSE);
                    ++pinfo->nCount;
                } else{
                    AcquireSRWLockExclusive(&glock);
                    sock_queue.push_back(psock);
                    ReleaseSRWLockExclusive(&glock);
                }

                //重置
                WSAResetEvent(pinfo->event_arr[0]);
            }
            else{
                WSAEnumNetworkEvents(pinfo->sock_obj_arr[i+1]->sock,pinfo->event_arr[i],&net_ev);

                // 讀事件 ,隨意處理, 這裏還可以加入線程池來處理 recv , send
                if(net_ev.lNetworkEvents & FD_READ){
                    if( net_ev.iErrorCode[FD_READ_BIT] != 0){
                        //出錯了
                    }
                    len = recv(pinfo->sock_obj_arr[i+1]->sock,pinfo->sock_obj_arr[i+1]->buf,SOCK_OBJ::BUFFSIZE-1,0);
                    pinfo->sock_obj_arr[i+1]->buf[len] = 0;
                    printf("sock:%ld , buf:%s\n",pinfo->sock_obj_arr[i+1]->sock,pinfo->sock_obj_arr[i+1]->buf);
                }

                    //關閉事件
                if (net_ev.lNetworkEvents & FD_CLOSE){
                    if(net_ev.iErrorCode[FD_CLOSE_BIT] != 0){
                        //出錯了
                    }
                    delete pinfo->sock_obj_arr[i+1];
                    WSACloseEvent(pinfo->event_arr[i]);
                    AcquireSRWLockExclusive(&pinfo->lock);
                    --pinfo->nCount;
                    rebulid(pinfo,i,pinfo->nCount);
                    ReleaseSRWLockExclusive(&pinfo->lock);
                }
            }

        }
    }


    return 0;
}


//創建一些線程,用於等待除了監聽socket以外的 事件
void create_thread_pool(unsigned poolsize = 4){
    for (int i = 0; i< poolsize; ++i) {
        unsigned int tid = 0;
        THREAD_INFO * p = new THREAD_INFO;
        _beginthreadex(0,0,workthread,p,0,&tid);
        gmap.insert(make_pair(tid,p));
    }
}


//用於分配的線程, 用條件變量來通知 一個新的client來了
unsigned int __stdcall dispath_thread(void * arg){
    puts("process sock thread start");
    SOCKET sock = INVALID_SOCKET;
    BOOL awaked = FALSE;
    InitializeSRWLock(&glock);
    while(1){
        EnterCriticalSection(&cs_queue);
        while(!bEnd && gqueue.empty()){
            SleepConditionVariableCS(&cond_queue,&cs_queue,INFINITE);
        }
        if(bEnd){
            LeaveCriticalSection(&cs_queue);
            break;
        }

        //取出client socket 
        sock = gqueue.front();
        gqueue.pop_front();
        LeaveCriticalSection(&cs_queue);

        //創建一個socket對象
        PSOCK_OBJ pobj = new SOCK_OBJ(sock);
        AcquireSRWLockExclusive(&glock);
        sock_queue.push_back(pobj);
        ReleaseSRWLockExclusive(&glock);


        // 從線程信息對象中去找 哪個線程還能存放,就放進去
begin_awake:
        awaked = FALSE;
        auto iter = gmap.begin();
        for(;iter != gmap.end() ; ++iter){
            if( iter->second->aviliable() ){
                iter->second->awake();
                awaked = TRUE;
                break;
            }
        }
        if(!awaked){
            create_thread_pool();
            goto begin_awake;
        }
    }

    puts("dispath done!");
    return 0;
}

int main(int argc, char *argv[])
{

    InitializeCriticalSectionAndSpinCount(&cs_queue,4000);
    InitializeConditionVariable(&cond_queue);
    WSADATA wsadata;
    WSAStartup(MAKEWORD(2,2) , &wsadata);
    SOCKET listenfd = socket(AF_INET, SOCK_STREAM,0);
    SOCKADDR_IN serv_addr = {0};
    serv_addr.sin_addr.S_un.S_addr = INADDR_ANY;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    bind(listenfd,(SOCKADDR*)&serv_addr, sizeof(serv_addr));
    listen(listenfd,10);
    WSAEVENT listen_ev = WSACreateEvent();
    WSAEventSelect(listenfd,listen_ev,FD_ACCEPT);
    int pos = 0;
    WSANETWORKEVENTS net_ev;
    int clientfd = 0;
    while(1){
        pos = WSAWaitForMultipleEvents(1,&listen_ev,FALSE,WSA_INFINITE,FALSE);
        if( WSA_WAIT_FAILED == pos || WSA_WAIT_TIMEOUT == pos){
            printf("WSAWaitForMultipleEvents failed !\n");
            continue;
        }
        WSAEnumNetworkEvents(listenfd,listen_ev,&net_ev);
        if( net_ev.lNetworkEvents & FD_ACCEPT){
            if (net_ev.iErrorCode[FD_ACCEPT_BIT] != 0 )  {
                printf("accept events failed! error:%ld\n" , GetLastError());
                continue;
            }
            clientfd = accept(listenfd,NULL,NULL);
            EnterCriticalSection(&cs_queue);
            gqueue.push_back(clientfd);
            LeaveCriticalSection(&cs_queue);
            WakeConditionVariable(&cond_queue);
        }
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章