Multi-task concurrent processing server (two) --- thread pool


Thread concurrency can lead to excessive thread creation, thread creation in order to solve too many, we introduced the concept of the pool.

Thread pool concept

  1. How it works: At the beginning of the program starts, it creates multiple (3 to 10) threads, created by the system or administrator. When a client connects, the deployment of a thread pool in client service for this purpose.
  2. And comparison of multi-threaded:
    (1) there will be no multi-thread creation and destruction, it is created before the start, the program exits destroyed.
    (2) limited thread, by the client-side control, speed. The client came to it allocation

Code implementation ideas:

  1. General framework or socket (), bind (), listen (), while (1) {accept (), recv (), send ()}
  2. 5 threads created when the program begins, created by pthread_create ().
  3. Define an array of file descriptors stored, it is necessary to provide a method: initialize (array elements are initialized to -1), is inserted, obtaining (search and returns a valid file descriptor, and the save array is set to -1). This array is shared by all threads, so we can think of a question: If the main thread receives multiple clients too fast, and multiple clients to get a file descriptor, then it is equal to a multiple client service description Fu, this is definitely wrong, so we need to take synchronization mechanisms to control it. Let insertion and retrieval functions to achieve mutual exclusion, but only one thread access. So we use a mutex pthread_mutex_t mutex, lock pthread_mutex_lock (& ​​mutex) when inserted into the function, take the lock pthread_mutex_unlock (& ​​mutex) exit, get function the same way.
  4. You must specify the entry address when creating a thread, once created, the thread up and running on their own, the thread must be blocked in certain conditions, wait for the main thread receives a wake-client connections.
    (1) The main thread to obtain the client connection file descriptor passed to the function thread (the thread sharing of resources, so you can define global variables, because multiple threads, so we define a global array to function as the main thread thread file descriptor transfer mechanisms)
  5. The main thread waits an accept client connections, if there is a link, after receiving the connector, this connector thread passed to the function, a wake-up thread pool to handle this connection.
  6. We can see that there is a thread to arouse and blocking, so the introduction of synchronization mechanisms, we use semaphores PV operation to achieve. 0 is not initially connected to the client, first with P (sem_wait (& sem)) it blocked receive client connection, V (sem_post (& sem)) wake-up operation. 0 is the beginning, the obstruction, when the main thread connection is received, V semaphore operation + 1, P -1 semaphore operation time will be processed once, the other continues blocking
  7. Therefore, the main process is this: the client and establish a connection, V operation awakened thread, take a file descriptor from the array, go with it. All clients processed end all threads, the main thread is the end of all thread ends.
    We draw a diagram to understand it:
    Here Insert Picture Description

Code flowchart appreciated

We have to sort out ideas, draw a flow chart of the code:

Here Insert Picture Description

Code

We write code, client code as before
threadPoll.c

# include<stdio.h>
# include<stdlib.h>
# include<string.h>
# include<unistd.h>
# include<assert.h>

# include<sys/types.h>
# include<pthread.h>
# include<sys/socket.h>
# include<arpa/inet.h>
# include<netinet/in.h>
# include<signal.h>
# include<semaphore.h>

#define THREAD_NUM 5//线程池的线程数量
#define BUFFSIZE 10 //文件描述符数量
sem_t sem;//信号量
pthread_mutex_t mutex; //互斥锁

int Buff_Fd[BUFFSIZE]; //线程共享。

//针对文件描述符的操作
//初始化
void InitBuffFd()
{
    int i=0;
    for(;i<BUFFSIZE;i++)
    {
        Buff_Fd[i]=-1;
    }
}

//插入,防止在插入时有另一个线程获取,所以加上互斥量
void InsertBuffFd(int fd)
{
    pthread_mutex_lock(&mutex);//加锁
    int i=0;
    for(;i<BUFFSIZE;++i)
    {
        if(Buff_Fd[i]==-1)
        {
            Buff_Fd[i]=fd;
            break;
        }
    }
    pthread_mutex_unlock(&mutex);//去锁

}
//获得文件描述符
int GetBuffFd()
{
    pthread_mutex_lock(&mutex);
    int i=0,fd=-1;
    for(;i<BUFFSIZE;++i)
    {
        if(Buff_Fd[i]!=-1)
        {
            fd=Buff_Fd[i];
            Buff_Fd[i]=-1;
            break;
        }
    }
    pthread_mutex_unlock(&mutex);
    return fd;
}
int InitSocket()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd==-1) return -1;

    struct sockaddr_in ser;
    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 res=bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
    if(res==-1) return -1;

    res=listen(sockfd,5);
    if(res==-1) return -1;

    return sockfd;
}
//线程工作
void* work_thread(void* arg)
{
    while(1)
    {
        //P操作阻塞它
        sem_wait(&sem);
        //获取文件描述符
        int fd=GetBuffFd();
        if(fd==-1) continue;
        //完成数据的收发
        while(1)
        {
            char buff[128]={0};
            int n=recv(fd,buff,127,0);
            if(n<=0)
            {
                printf("one client over\n");
                close(fd);
                break;
            }
            printf("%d:%s\n",fd,buff);
            int res=send(fd,"OK",2,0);
            if(res<=0)
            {
                printf("send error\n");
                close(fd);
                break;
            }
        }

    }
}
int main()
{
    //初始化信号量
    sem_init(&sem,0,0);
    //初始化互斥锁
    pthread_mutex_init(&mutex,NULL);
    //初始化描述符数组
   InitBuffFd();

   int sockfd=InitSocket();
   //创建线程池
   pthread_t id[THREAD_NUM];
   int i=0;
   for(;i<THREAD_NUM;++i)
   {
       int res=pthread_create(&id[i],NULL,work_thread,NULL);
       assert(res==0);
   }
   while(1)
   {
       struct sockaddr_in cli;
       socklen_t len=sizeof(cli);
       int c=accept(sockfd,(struct sockaddr*)&cli,&len);
       if(c<0)
       {
           continue;
       }
        printf("%d连接\n",c);
        //把C插入数组中
        InsertBuffFd(c);
        //启动一个线程去处理它,V操作
        sem_post(&sem);
   }
   close(sockfd);
   exit(0);

}

Show

Operational and client service:
Here Insert Picture Description
we can clearly see the success, but also see its flaws, we set the number of threads in the code is 5, which means that it handles up to five client side, more to die, hence the sixth successful connection, but the data can not be processed. We tried to close one:
Here Insert Picture Description

So defect thread pool is: you can only deal with the client size of the array, to solve this problem can be introduced I / O multiplexing, can monitor multiple clients, ready to handle an event the size of the array.
Come on! ✍.

Published 54 original articles · won praise 8 · views 5288

Guess you like

Origin blog.csdn.net/qq_43411555/article/details/105320130