Linux での C 言語によるマルチスレッド プログラミングの詳細な紹介

黄舟
リリース: 2017-10-14 11:00:42
オリジナル
2138 人が閲覧しました

この記事では主に Linux での C 言語でのマルチスレッド プログラミングを紹介します。必要な方は参考にしてください

Linux サービスを作成するとき、プログラムのパフォーマンスを向上させるために Linux マルチスレッド テクノロジをよく使用します

少しだけ。マルチスレッドに関する知識:

アプリケーションは複数のスレッドを開始できます。

スレッド (軽量プロセス、LWP) は、プログラム実行の最小単位です。

一般に、最も単純なプログラムには少なくとも 1 つのスレッドがあり、これはプログラム自体、つまり main 関数です (シングルスレッドプロセスは、単純にスレッドが 1 つだけあるプロセスと考えることができます)

1 つのスレッドをブロックする他のスレッドには影響しません。

マルチスレッドプロセスは、システムの CPU リソースを可能な限り利用できます。

1 スレッドを作成する

まず、プロセス内にスレッドを作成する簡単なコードから始めて、次に詳しく見ていきましょう。


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 printf("func run...\n");
 return NULL;
}
int main()
{
 pthread_t t1;
 int err = pthread_create(&t1,NULL,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 sleep(1);
 return EXIT_SUCCESS;
}
int pthread_create(pthread_t *thread,const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
ログイン後にコピー

main関数で上記の関数を呼び出してスレッドを作成します。

関数パラメータ:

最初のパラメータ: pthread_t は、作成されたスレッドの一意の識別子を表します。これは、構造体を作成した後、この構造体のポインタを渡す必要があります。

2番目のパラメータ: pthread_attr_t は、割り当てスタックのサイズなど、このスレッドを作成するためのいくつかの設定を表します。 。通常、スレッド作成のデフォルト設定を表す NULL を入力できます。 スレッドの作成時にこの関数が呼び出されます。この関数の戻り値は void* です。関数のパラメータも void* です。 一般的な形式は次のとおりです

void * func(void * arg){} 4 番目のパラメータ: 3 番目の関数の呼び出しによって渡されたパラメータを表します

関数の戻り値: 関数は正常に 0 を返します。が 0 に等しくない場合は、関数呼び出しが失敗したことを意味します。この場合、strerror (errno) を使用すると、特定のエラーが出力されます。

注: 各スレッドには errno のコピーがあり、異なるスレッドには異なる errno があります

最終的には gcc を通じてコン​​パイルされます

gcc 1createthread.c -c -o 1createthread.o
gcc 1createthread.o -o thr1 -lpthread
ログイン後にコピー

コンパイル時に、 -lpthread を追加して libpthread.so 動的ライブラリをリンクする必要があります。それ以外の場合は、関数が見つからないというプロンプトが表示されます

関数呼び出しは結果を返します

質問: なぜスリープ関数が呼び出されるのですか

答え: おそらく、新しく作成されたスレッドがスレッドに到達する前にメインスレッドが終了する可能性があります。メインスレッドが終了すると、すべてのスレッドが終了します。

2 スレッドがハングします

スレッド内に別のスレッドを作成することがありますが、メインスレッドは、作成されたスレッドが戻り、スレッドの戻り値を取得してから終了するまで待機する必要があります。このとき、スレッドの一時停止を使用する必要があります。

int pthread_join(pthread_t th, void **thr_return);。
ログイン後にコピー

pthread_join 関数は、 th で指定されたスレッドが終了するまで現在のスレッドを一時停止するために使用されます。


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int i=0;
 for(;i<5;i++)
 {
  printf("func run%d\n",i);
  sleep(1);
 }
 int * p = (int *)malloc(sizeof(int));
 *p=11;
 return p;
}
int main()
{
 pthread_t t1,t2;
 int err = pthread_create(&t1,NULL,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 void *p=NULL;
 pthread_join(t1,&p);
 printf("线程退出:code=%d\n",*(int*)p);
 return EXIT_SUCCESS;
}
ログイン後にコピー

関数実行結果


メイン関数は作成したスレッドの実行終了を待ち、スレッド実行終了の戻り値を取得しました

3 スレッド終了

プロセスが () 関数を終了すると終了します。では、スレッドの終了とは何でしょうか? スレッド終了の 3 つの状況:

スレッドはスタートアップ関数から戻ったばかりで、戻り値はスレッドの終了コードです。

スレッドは、同じプロセス内の他のスレッドによってキャンセルされる可能性があります。

スレッドは pthread_exit を呼び出します。

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int i=0;
 while(1)
 {
  if(i==10)
  {
   int * p = (int *)malloc(sizeof(int));
   *p=11;
   pthread_exit(p);
  }
  printf("fun run %d\n",i++);
  sleep(1);
 }
 return NULL;
}
int main()
{
 pthread_t t1,t2;
 int err = pthread_create(&t1,NULL,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 void *p=NULL;
 pthread_join(t1,&p);
 printf("线程退出:code=%d",*(int*)p);
 return EXIT_SUCCESS;
}
void pthread_exit(void *arg);
ログイン後にコピー

pthread_exit 関数のパラメータは、通常のスレッドが終了してリターンするときと同じで、終了を待つメインスレッドによって取得されます。

関数の操作結果:

4スレッドのデタッチメント

int pthread_detach(pthread_t th);
ログイン後にコピー
pthread_detach関数は、スレッドをデタッチ状態にします。

スレッドを待機しておらず、スレッドの戻り値に興味がない場合は、スレッドをデタッチ状態に設定し、スレッドの終了時にシステムが占有しているリソースを自動的にリサイクルできるようにすることができます。

スレッドは、それ自体をデタッチ状態に変更するために pthread_detach を呼び出すことはできません。他のスレッドによってのみ pthread_detach を呼び出すことができます。

5 スレッドのキャンセル

int pthread_cancel(pthread_t th);
ログイン後にコピー
pthread_cancel 関数を使用すると、あるスレッドが th で指定された別のスレッドをキャンセルできます。

関数が成功した場合は 0 を返し、それ以外の場合は 0 以外を返します。

#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func1(void * arg)
{
 while(1)
 {
  printf("fun run...\n");
  sleep(1);
 }
 return NULL;
}
int main()
{
 pthread_t t1;
 if(pthread_create(&t1,NULL,func1,NULL)!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
  return -1;
 }
 sleep(5);
 pthread_cancel(t1);
 pthread_join(t1,NULL);
 return EXIT_SUCCESS;
}
ログイン後にコピー

関数の実行結果:


上面我们说过创建一个线程函数pthread_create的第二个参数,用来决定创建线程的一些初始化状态,这里我们 举个例子,改线程一创建就是分离状态的线程(

上面介绍了pthread_detach函数的概念,可以通过pthread_attr_t在创建线程的时候就指定线程属性为detach,而不用创建以后再去修改线程属性。

先上一段代码:


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int i=0;
 for(;i<5;i++)
 {
  printf("func run%d\n",i);
  sleep(1);
 }
 int * p = (int *)malloc(sizeof(int));
 *p=11;
 return p;
}
int main()
{
 pthread_t t1;
 pthread_attr_t attr;//申明一个attr的结构体
 pthread_attr_init(&attr);//初始化结构体
 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//设置线程为分离线程
 int err = pthread_create(&t1,&attr,func,NULL);
 if(err!=0)
 {
  printf("thread_create Failed:%s\n",strerror(errno));
 }else{
  printf("thread_create success\n");
 }
 pthread_attr_destroy(&attr);
 pthread_join(t1,NULL);
 printf("主线程退出\n");
 return EXIT_SUCCESS;
}
ログイン後にコピー

pthread_attr_t就是我们要传入的参数的结构体,一般申明的步骤有

1,申明一个pthread_attr_t对象

2,函数pthread_attr_init初始化attr结构。

3,设置线程的一些属性,比如pthread_attr_setdetachstate函数就是设置该线程创建的时候为正常状态还是分离状态。

4,函数pthread_attr_destroy释放attr内存空间

pthread_attr_setdetachstate把线程属性设置为下面两个合法值之一:

说明

PTHREAD_CREATE_DETACHED

设置线程为分离状态

PTHREAD_CREATE_JOINABLE

设置线程为正常状态

上面函数运行结果:

因为线程是个分离状态的,所以pthread_join挂起会失效,主线程很快运行结束,程序也就结束了,创建的线程还没来得及运行

线程同步

有时候我们多个线程处理订单扣减库存会遇到这样的问题,两个线程同时进入一段代码先查询库存,两个都查出来为还剩一件库存,第一个线程用掉这个库存后,将库存变为0,但是第二个线程刚才也查出来为1了,所以他还认为有库存,

这个时候操作就会引发我们想不到的意外,库存变为负数了!!所以这个时候就需要使用线程的同步!!

先上一段代码看看效果:


#include<pthread.h>
#include<stdio.h>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
void * func(void * arg)
{
 int threadno =*(int*)arg;
 int i=0;
 for(;i<10;i++)
 {
  printf("%d thread%d \n",threadno,i);
  sleep(1);
 }
 return NULL;
}
int main()
{
 pthread_t t1,t2;
 int i1=1,i2=2;
 pthread_create(&t1,NULL,func,&i1);
 pthread_create(&t2,NULL,func,&i2);
 pthread_join(t1,NULL);
 pthread_join(t2,NULL);
 printf("主线程退出\n");
 return EXIT_SUCCESS;
}
ログイン後にコピー

函数运行结果:

可以看到两个线程是没有规律的争相处理的,如果这段代码是扣减库存就完蛋啦!,所以我们要对这段代码进行加锁,同一时刻只能有一个线程进入操作!

先上代码:


#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void * func(void * arg)
{
 pthread_mutex_lock(&mutex);//对mutex加锁,其他线程进入后将会挂起,知道这个锁被解锁
 int threadno =*(int*)arg;
 int i=0;
 for(;i<10;i++)
 {
  printf("%d thread%d \n",threadno,i);
  sleep(1);
 }
 pthread_mutex_unlock(&mutex);
 return NULL;
}
int main()
{
 pthread_t t1,t2;
 int i1=1,i2=2;
 pthread_create(&t1,NULL,func,&i1);
 pthread_create(&t2,NULL,func,&i2);
 pthread_join(t1,NULL);
 pthread_join(t2,NULL);
 printf("主线程退出\n");
 return EXIT_SUCCESS;
}
ログイン後にコピー

函数运行结果:

可以看到第二个线程先进入后一直运行结束,对mutex解锁后,第一个线程才能进方法里面运行!否则会挂起,一直等到锁被解锁!

PTHREAD_MUTEX_INITIALIZER是初始化一个快速锁的宏定义。


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
ログイン後にコピー

加锁解锁函数:


int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
ログイン後にコピー

总结

以上がLinux での C 言語によるマルチスレッド プログラミングの詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート