マルチタスクの同時処理サーバー(2)---スレッドプール


スレッドの同時実行があまりにも多くを解決するために、過剰なスレッドの作成、スレッドの作成につながることができ、私たちはプールの概念が導入されました。

スレッドプールのコンセプト

  1. 仕組み:プログラムが起動の開始時に、それはシステムまたは管理者によって作成された複数(3〜10)のスレッドを作成します。クライアントは、この目的のためにクライアントサービスにおけるスレッドプールの展開を接続するとき。
  2. そして、マルチスレッドの比較:
    (1)は、マルチスレッドの作成と破棄は存在しません、それが開始する前に作成され、プログラムが終了が破壊されました。
    クライアント側制御、速度(2)限られたスレッド。クライアントは、割り当てに来ました

コードの実装のアイデア:

  1. 一般的なフレームワークまたはソケット()、バインド()、聞く()、一方、(1){受け入れる()、RECV()、(送信)}
  2. pthread_createのことで作成したプログラムの開始時に作成した5つのスレッド、()。
  3. 保存されたファイル記述子のアレイを画定する、方法を提供することが必要である:得、挿入されている、(配列要素が-1に初期化されている)を初期化する(有効なファイル記述子を検索して戻り、そして配列保存に設定されている-1)。我々は、質問を考えることができるように、この配列は、すべてのスレッドで共有されている:メインスレッドが速すぎて、複数のクライアント、およびファイル記述子を取得するための複数のクライアントを受信した場合、それは複数のクライアントサービスの説明と同じですフーは、これは間違いなく間違っているので、我々はそれを制御するために同期メカニズムを取る必要があります。相互排除、一つだけのスレッドへのアクセスを実現するために挿入し、検索機能をしてみましょう。我々は機能に挿入されたミューテックスpthread_mutex_tミューテックス、ロックpthread_mutex_lockの(&ミューテックス)を使用するので、ロックpthread_mutex_unlockの(&ミューテックス)出口を出て、機能を同じ方法で取得します。
  4. スレッドを作成するときは、エントリアドレスを指定する必要があり、作成した後、スレッドは特定の条件でブロックされなければならないまで、自分で実行しているスレッドは、メインスレッドの待機は、ウェイククライアント接続を受け取ります。
    (1)あなたはグローバル変数を定義することができるように、関数スレッド(リソースのスレッドの共有に渡されたクライアント接続のファイルディスクリプタを取得するためにメインスレッド、複数のスレッドなぜなら、私たちは、メインスレッドとして機能するグローバル配列を定義するように、スレッドファイルディスクリプタ転送メカニズム)
  5. コネクタを受け取った後、このコネクタのスレッドが機能、この接続を処理するためのウェイクアップスレッドプールに渡され、リンクがある場合、メインスレッドは、クライアント接続を受け入れるまで待機します。
  6. 私たちは、私たちが達成するためにセマフォのPV操作を使用し、喚起するスレッドとブロックがあることがわかり、その同期メカニズムを導入することができます。0は、最初のP(sem_wait(&SEM))で、それはクライアントの接続、V(sem_post(&SEM))ウェイクアップ動作を受ける遮断、最初にクライアントに接続されていません。0は、他のブロッキング続け、メインスレッドの接続を受信し始め、閉塞、Vセマフォ操作+ 1、P -1セマフォ操作時間が一度に処理されています
  7. そのため、メインプロセスはこれです:クライアントとの接続、V操作目覚めスレッドを確立し、配列からのファイルディスクリプタを取り、それを持って行きます。すべてのクライアントがすべてのスレッドを終了処理し、メインスレッドは、すべてのスレッドが終了するの終わりです。
    我々はそれを理解するための図を描きます:
    ここに画像を挿入説明

コードは理解フローチャート

私たちは、アイデアを整理、コードのフロー・チャートを描画する必要があります。

ここに画像を挿入説明

コード

私たちは、以前のように、コード、クライアントのコードを書く
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);

}

ショー

運用および顧客サービス:
ここに画像を挿入説明
私たちははっきりと成功を参照するだけでなく、その欠陥を見ることができ、我々は、コード内のスレッド数を設定するには、それは、より5つのクライアント側まで処理する5、手段であり、ダイ、したがって第六の成功した接続が、データを処理することができません。私たちは、近いものにしようとしました。
ここに画像を挿入説明

だから、欠陥スレッドプールがある:あなただけのこの問題を解決するために、配列のクライアントのサイズを扱うことができる多重化I / Oを導入することができ、イベント配列のサイズを処理する準備ができて、複数のクライアントを、監視することができます。
さあ!✍。

公開された54元の記事 ウォンの賞賛8 ビュー5288

おすすめ

転載: blog.csdn.net/qq_43411555/article/details/105320130