線程池

一組資源的集合。爲了提高服務器性能,以空間換時間,即“浪費”服務器的硬件資源,以換取其運行效率。

靜態資源分配方式:資源在服務器啓動之初就被完全創建好並初始化。
對於預期應該分配多少資源,有兩種方案:

  • 分配“足夠多”的資源,即針對每個可能的客戶連接都分配必要的資源,但會導致資源的浪費;
  • 預先分配一定的資源,此後發現資源不夠用,就再動態分配一些加入池中。

    池可以看作是服務器管理系統資源的應用層設施,避免了服務器對內核的頻繁訪問。因爲服務器在處理客戶請求時,如果它需要相關的資源,可以直接從池中獲取,無需動態分配,而且直接從池中獲取資源比動態分配資源速度快,動態分配需要系統調用。


根據不同的資源類型,池分爲多種,

  • 內存池
  • 進程池
  • 線程池
  • 連接池

內存池:用於socket的接收緩存和發送緩存。對於某些長度有限的客戶請求,比如HTTP請求,預先分配一個大小足夠的接收緩存區是合理的。當客戶請求的長度超過接收緩衝區的大小時,可以選擇丟棄請求或動態擴大接收緩衝區。

進程池和線程池都是用於併發編程。當需要一個工作進程或工作線程來處理新到的客戶請求時,可以直接從進程池或線程池中取得一個執行實體,而無需動態調用fork或pthread_create等函數來創建進程或線程。

連接池:是服務器預先和數據庫程序建立的一組連接的集合。用於服務器或服務器機羣的內部永久連接。當某個邏輯單元需要訪問數據庫時,它可以直接從連接池中取得一個連接的實體並使用。待訪問完數據庫,邏輯單元再將該連接返還給連接池。


以線程池實現爲例: 線程池的實現有多種方式,主要區別點在於主線程完成那些工作:

1、主線程負責對監聽socket和所有的連接socket進行I/O複用,工作線程負責和有通訊需求的客戶端通訊。如下圖所示:
這裏寫圖片描述

2、主線程僅僅負責監聽socket,連接socket的接受以及與客戶端通訊都有工作線程完成。如下圖所示:
這裏寫圖片描述

由於同一個進程的所有線程之間共享所有打開的文件描述符、全局區數據。所以,線程之間傳遞數據簡單的多。在此,我們實現第一種情況下的線程池。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <epoll.h>

#define MAX 128
int epfd = -1;

sem_t sem;
int fds[MAX];
int count = 0;

void initfds()
{
    int i = 0;
    for(;, i < MAX; ++i)
    {
        fds[i] = -1;
    }
}

int getfds()
{
    if(fds[0] != -1)
    {
        int c = fds[0];
        int i = 0;
        for(; i < count && i < MAX - 1; ++i)
        {
            fds[i] = fds[i + 1];
        }
        fds[MAX - 1] = -1;

        return c;
    }
}

//將文件描述符設置成非阻塞的
int setnonblocking(int fd)
{
    int old = fcntl(fd, F_GETFL);
    int new = old | O_NONBLOCK;
    fcntl(fd, F_SETFL, new);
    return old;
}

void * thread_fun(void *arg)
{
    sem_wait(&sem);
    int fd = getfds();
    while(1)
    {
        char buff[1024] = {0};
        int n = recv(fd, buff, 1023, 0);
        if(n < 0)
        {
            if((error == EAGAIN) || (error == EWOULDBLOCK))
            {
                send(fd, "OK", 2, 0);
                break;
            }
            epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
            close(fd);
            break;
        }
        if(n == 0)
        {
            epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
            close(fd);
            break;
        }

        printf("%d: %s\n", fd, buff);    
    }
}

int main()
{
    sem_init(&sem, 0, 0);
    initfds();

    int i = 0;
    for(; i < 3; ++i)
    {
        pthread_t id;
        int ret = pthread_create(&id, NULL, thread_fun, NULL);
        assert(ret == 0);
    }

    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    assert(listenfd != -1);

    struct sockaddr_in  ser, cli;
    memset(&ser, 0, sizeof(ser));
    ser.sin_family = AF_INET;
    ser.sin_port = htons(6000);
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");

    int ret = bind(listenfd,  (struct sockaddr*)&ser, sizeof(ser));
    assert(ret != -1);

    listen(listenfd, 5);

    epfd = epoll_create(5);
    assert(epfd != -1);

    struct epoll_event  event;
    event.events = EPOLLIN;
    event.data.fd = listenfd;

    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &event);

    struct epoll_event events[MAX];

    while(1)
    {
        int n = epoll_wait(epfd, events, MAX, -1);
        if(n < 0)
        {
            exit(0);
        }
        else if(n == 0)
        {
            printf("timeout\n");
            continue;
        }
        else
        {
            int i = 0;
            for(; i < n; ++i)
            {
                int fd = events[i].data.fd;
                if(fd == listenfd)
                {
                    int len = sizeof(cli);
                    int c = accept(fd, (struct sockaddr*)&cli, &len);
                    assert(c != -1);
                    event.events = EPOLLIN | EPOLLET;
                    event.data.fd = c;
                    epoll_ctl(epfd, EPOLL_CTL_ADD, c, &event);
                    setnonblockint(c);
                }
                else
                {
                    if(count < MAX)
                    {
                        fds[count++] = fd;
                     }
                    else
                    {
                        printf("queue full\n");
                        continue;
                    }
                    sem_post(&sem);
                }
            }
        }
    }
}
發佈了40 篇原創文章 · 獲贊 6 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章