LinuxCでのスレッドプールの使用について簡単に説明します

スレッドプールもマルチスレッド処理方式です。「プロデューサー」スレッドによって提案されたタスクを「タスクキュー」に追加すると、一部のスレッドは「タスクキュー」のタスクを自動的に完了します。

LinuxCでのスレッドプールの使用について簡単に説明するLinuxCでのスレッドプールの使用について簡単に説明する

マルチスレッドプログラミング、スレッドの作成、特定のタスクの完了を指定し、スレッドが終了するのを待ちます。プログラミングのニーズは満たすことができますが、多数のスレッドを作成する必要がある場合、作成プロセスやスレッドの破棄プロセス中に大量のCPUが消費される可能性があり、オーバーヘッドが大幅に増加します。例:フォルダのコピー、WEBサーバーの応答。

スレッドプールは、これと同様の問題を解決するために使用されます。これにより、スレッドの頻繁な作成と破棄によって引き起こされるオーバーヘッドを減らすことができます。

スレッドプールテクノロジーのアイデア:通常、事前に作成されたスレッドテクノロジーが使用されます。つまり、特定の数のスレッドを事前に作成する必要があります。これらのスレッドを事前に作成した後、「タスクキュー」にタスクがない場合は、これらのスレッドをスリープ状態にします。タスクができたら、スレッドをウェイクアップしてタスクを実行します。タスクの実行後は、次のことを行う必要はありません。必要なときまでスレッドを破棄する終了またはシャットダウンするとき、この時点で、スレッドプールを破棄する関数を呼び出してスレッドを破棄します。

スレッドがタスクを完了した後、スレッドは破棄されませんが、次のタスクが自動的に実行されます。さらに、タスクが多い場合は、スレッド数を増やすための機能インターフェイスを使用でき、タスクが少ない場合は、一部のスレッドを破棄するための機能インターフェイスを使用できます。

スレッドを作成および破棄する時間がタスクの実行時間と比較してごくわずかである場合、この場合はスレッドプールを使用する必要はありません。

「タスクキュー」は共有リソース「相互に排他的なアクセス」です

LinuxCでのスレッドプールの使用について簡単に説明するLinuxCでのスレッドプールの使用について簡単に説明する

スレッドプールは本質的にデータ構造であり、それを記述するための構造が必要です。

struct pthread_pool //スレッドプールの実装
{ 
 //一般的に次のメンバーがあります
 
 //この「タスクキュー」を保護するためのミューテックス
 pthread_mutex_tlock; //ミューテックスロック  
  
 //スレッド条件変数は「タスクキュー」タスクがあるかどうかを示します
 pthread_cond_t cond; //条件変数
  
 boolshutdown; //プログラムを終了するかどうかを示しますbool:type false / true 
 
 //タスクキュー(リンクリスト)、ポイントする必要がある最初のタスクをポイントします
 //すべてのスレッドが取得されますタスクリンクリストからタスク「共有リソース」
 structtask * task_list; 
  
 //スレッドプールには複数のスレッドがあり、各スレッドにはtidがあり、tidを保存するには配列が必要です
 pthread_t * tids; // malloc()   
  
 //スレッドプールは、スレッド数、現在のスレッド数
 unsigned intactive_threads;
  
 最大数のジョブキュースレッドプールタスク// 
 unsigned intmax_waiting_tasks;
  
 スレッドプールタスクキュー//現在
 署名されていないタスクの数intcur_waiting_tasks; 
  
 // ...... 
 
};
  
//タスクキュー(リンクリスト)上のタスクノードは、タスクを適切に記述できる限り、
//スレッドは引き続きタスクキューからタスクを取得します
struct task // task node   
{ 
 // 1.タスクノードによって表されるタスクである「関数ポインタ」は、タスクによって実行される関数を指します(cp_file)
 void *(* do_task)(void * arg); 
  
 / / 2。ポインタ、タスクポインティング関数(ファイル記述子)のパラメータを指す
 void * arg; 
  
 // 3。次のタスク
 構造体を指すタスクノードタイプのポインタstructtask * next; 
};

スレッドプールフレームワークのコードは次のとおりで、関数は自己入力されます。

スレッドプールの操作に必要な関数インターフェイス:pthread_pool.c、pthread_pool.h

「スレッドプール」をアウトソーシング会社と考えてください。達成する必要があるのは、スレッドプールによって提供される機能インターフェイスを操作することです。

pthread_pool.c

#include "pthread_pool.h" 
 
/ * 
 init_pool:スレッドプール初期化関数、指定されたスレッドプールの
 thread_num初期スレッドを初期化します@pool:ポインター、
 初期化するスレッドプールポイントします@threa_num:初期化するスレッドプール内開始されたスレッドの数
 戻り値:  
  成功0
  失敗-1 
* / 
 
int init_pool(* pthread_pool the pool、unsigned int threa_num)
{
 構造//スレッドプールの
  
 初期化//スレッドミューテックスの初期化
 pthread_mutex_init(&pool-> lock、NULL ) ; 
  
 //スレッド条件変数を初期化します
 pthread_cond_init(&pool-> cond、NULL); 
 
 pool-> shutdown = false; //終了しないでください
 
 pool-> task_list =(struct task *)malloc(sizeof(struct task)); 
 
 pool -> tids =(pthread_t *)malloc(sizeof(pthread_t)* MAX_ACTIVE_THREADS); 
 if(pool-> task_list == NULL || pool-> tids == NULL)時間== NULL) 
 {
  perror( "malloc memery error"); 
  return -1; 
 } 
 
 pool-> task_list-> next = NULL; 
 
 //開始時にスレッドプールで提供するために初期化されるスレッドの数
 pool-> active_threads = threa_num; 
 
 //示しますスレッドプール最大で
 いくつのタスクがありますかpool-> max_waiting_tasks = MAX_WAITING_TASKS; 
 
 //スレッドプールプールのタスクキューにある現在のタスク数
 -> cur_waiting_tasks = 0; 
 
 // thread_numスレッドを作成し、スレッドに実行させますタスク割り当て関数、
 //すべてを記録するスレッドのtid 
 int i = 0; 
 for(i = 0; i <threa_num; i ++)
 { 
  int ret = pthread_create(&(pool-> tids)[i]、NULL、routine 、(void *)pool); 
  if(ret!= 0)
  { 
   perror( "create thread error"); 
   return -1; 
  } 
 
  printf( "[%lu]:[%s] ===> tids [%d ]:[%lu] "、pthread_self()、
   __FUNCTION __、i、pool-> tids [i]); 
 } 
 
 return 0; 
} 
 
/ *
 ルーチン:タスク割り当て関数。
  すべてのスレッドがこの機能の実行を開始しています。この機能はタスクキュースレッドプールから続行され、
  実行中のタスクノードを削除します。
   
  タスクノードには「関数ポインタ」h「関数パラメータ」が含まれます
* / 
 
void * routine(void * arg)
{ 
 // argはスレッドプールのポインタを表します
  
 while()
 { 
  //スレッドミューテックスロックを取得し、ロックします  
   
  //スレッドプールが終了していない場合、ノードはスレッドプールのタスクキューから継続的に削除され、
  //実行されます。
   
  //スレッドミューテックスを解放し、ロックを解除します
   
  //タスクノードを解放します
 } 
} 
 
/ * 
 destroy_pool:スレッドプールを破棄し、破棄する前にすべてのタスクが完了していることを確認します
* / 
 
int destroy_pool(pthread_pool * pool)
{ 
 //待機中のすべてのスペースを解放しますタスクが完了しました(参加)。
 //すべてのスレッドをウェイクアップします
 //結合関数を使用して、各スレッドリソースを再利用します。
}
 
/ * 
 add_task:タスクをタスクキューに追加し、do_task(関数ポインター)が指す
  タスクとargが指すパラメーターをタスクノードに保存して、プールタスクキューに追加します。
   
 @pool:タスクを追加するスレッドプール
 @do_task:追加する必要のあるタスク(cp_file)
 @arg:実行するタスクのパラメーター(ファイル記述子)
* / 
 
int add_task(pthread_pool * pool、 void *(* do_task)(void * arg)、void * arg)
{ 
 // 2番目と3番目のパラメーターを
  
 structtaskにカプセル化し、   //それをpool-> task
  
 タスクキューに追加します。//タスクキューに注意してください。は1つの共有リソースです
  
 //タスクの後に待機中のスレッドをウェイクアップする場合。
} 
 
//タスクが多い場合は、スレッドプールにスレッドを追加しますpthread_create 
int add_threads(pthread_pool * pool、unsigned int num); 
{ 
 // numスレッドを作成し、各スレッドにスレッド展開関数を実行させます
  
 //新しく作成されたスレッドtid 、プールに追加-> tids   
} 
 
//タスクが少ない場合は、スレッドプール内のスレッド数を減らしますpthread_cancel join
int remove_threads(pthread_pool * pool、unsigned int num)
{ 
 // pthread_cancelを使用してnumスレッドをキャンセルし  
 ます// pthread_join関数を使用してリソースを再利用します。
}

pthread_pool.h

#ifndef __PTHREAD_POOL_H__ 
#define __PTHREAD_POOL_H __ 
 
//スレッドプール内のスレッド
の最大数を示します
#defineMAX_ACTIVE_THREADS 20 
 //スレッドプール内のタスクの最大数を示します
#defineMAX_WAITING_TASKS 1024 
 
//タスクキューのタスクノード(リンクされたリスト)、タスクを記述できる限り、
//スレッドはタスクキューからタスクを継続的にフェッチします
struct task // task node   
{ 
 // 1.タスクノードによって表されるタスク、「関数ポインタ」はタスクによって実行される関数(cp_file)
 void *(* do_task)(void * arg); 
  
 // 2.ポインター、タスクポインティング関数(ファイル記述子)のパラメーターを指す
 void * arg; 
  
 // 3。タスクノードタイプのポインタ、次のタスクを指す
 struct task * next; 
}; 
 
struct pthread_pool //スレッドプールの実装
{ 
 //一般的に次のメンバーがあります
 
 //この「タスクキュー」を保護するためのミューテックスロック
 pthread_mutex_tロック; //ミューテックスロック  
  
 //スレッド条件変数表現「タスクキュー」にはタスクがありますか
 pthread_cond_tcond; //条件変数
  
 bool shutdown; //プログラムを終了するかどうかを示しますbool:type false / true 
 
 //タスクキュー(リンクリスト)、ポイントする必要がある最初のタスクをポイントします
 //すべてのスレッドはタスク「共有リソース」をから取得します
 tasklinked list struct task * task_list; 
  
 //スレッドプールには複数のスレッドがあり、各スレッドにはtidがあり、tidを保存するには配列が必要です
 pthread_t * tids; // malloc()   
  
 //サービス中のスレッドの数スレッドプール、現在のスレッド数
 unsigned int active_threads; 
  
 //スレッドプールタスクキュー内のタスクの最大数
 unsignedint max_waiting_tasks; 
  
 //現在スレッドプールタスクキューにあるタスクの数
 unsignedint cur_waiting_tasks; 
  
 //。 .. 
 
}; 
 
/ * 
 init_pool:スレッドプール初期化関数は指定されたスレッドプールを初期化します。thread_num
  初期スレッド
 @pool:初期化するスレッドプールへのポインター
 @threa_num:開始を初期化するスレッドプール内のスレッド数
 戻り値:  
  成功時には0
  失敗-1
* / 
//タスクが多い場合は、スレッドプールにスレッドを追加しますpthread_create
 
int init_pool(pthread_pool * pool、unsigned int threa_num); 
 
/ *
 ルーチン:タスク割り当て関数。
  すべてのスレッドがこの機能の実行を開始しています。この機能はタスクキュースレッドプールから続行され、
  実行中のタスクノードを削除します。
   
  タスクノードには「関数ポインタ」h「関数パラメータ」が含まれます
* / 
 
void * routine(void * arg); 
 
/ * 
 destroy_pool:スレッドプールを破棄し、破棄する前にすべてのタスクが完了していることを確認します
* / 
 
int destroy_pool(pthread_pool * pool) ; 
 
/ * 
 add_task:タスクをタスクキューに追加し、do_task(関数ポインター)が指す
  タスクとargが指すパラメーターをタスクノードに保存して、プールタスクキューに追加します。
   
 @pool:タスクを追加するスレッドプール
 @do_task:追加する必要のあるタスク(cp_file)
 @arg:実行するタスクのパラメーター(ファイル記述子)
* / 
 
int add_task(pthread_pool * pool、 void *(* do_task)(void * arg)、void * arg);
 
int add_threads(pthread_pool * pool、unsigned int num);
 
 
//タスクが少ない場合は、スレッドプール内のスレッド数を減らしますpthread_cancel join 
int remove_threads(pthread_pool * pool、unsigned int num); 
 
#endif

おすすめ

転載: blog.csdn.net/yaxuan88521/article/details/113836237