ホームページ 運用・保守 Linuxの運用と保守 Linux での C 言語によるマルチスレッド プログラミングの詳細な紹介

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

Oct 14, 2017 am 11:00 AM
linux プログラミング 詳しい

この記事では主に 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 サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Redisでサーバーを開始する方法 Redisでサーバーを開始する方法 Apr 10, 2025 pm 08:12 PM

Redisサーバーを起動する手順には、以下が含まれます。オペレーティングシステムに従ってRedisをインストールします。 Redis-Server(Linux/Macos)またはRedis-Server.exe(Windows)を介してRedisサービスを開始します。 Redis-Cli ping(Linux/macos)またはRedis-Cli.exePing(Windows)コマンドを使用して、サービスステータスを確認します。 Redis-Cli、Python、node.jsなどのRedisクライアントを使用して、サーバーにアクセスします。

単一のスレッドレディスの使用方法 単一のスレッドレディスの使用方法 Apr 10, 2025 pm 07:12 PM

Redisは、単一のスレッドアーキテクチャを使用して、高性能、シンプルさ、一貫性を提供します。 I/Oマルチプレックス、イベントループ、ノンブロッキングI/O、共有メモリを使用して同時性を向上させますが、並行性の制限、単一の障害、および書き込み集約型のワークロードには適していません。

Linuxの5つの柱:彼らの役割を理解する Linuxの5つの柱:彼らの役割を理解する Apr 11, 2025 am 12:07 AM

Linuxシステムの5つの柱は次のとおりです。1。Kernel、2。SystemLibrary、3。Shell、4。FileSystem、5。SystemTools。カーネルはハードウェアリソースを管理し、基本的なサービスを提供します。システムライブラリは、アプリケーション用の事前コンパイルされた機能を提供します。シェルは、ユーザーがシステムと対話するインターフェイスです。ファイルシステムはデータを整理して保存します。また、システムツールはシステム管理とメンテナンスに使用されます。

Oracleのインスタンス名を表示する方法 Oracleのインスタンス名を表示する方法 Apr 11, 2025 pm 08:18 PM

Oracleでインスタンス名を表示するには3つの方法があります。「sqlplus」と「v $ instanceからselect instance_name;」を使用します。」コマンドラインのコマンド。 「show instance_name;」を使用しますSQL*Plusのコマンド。オペレーティングシステムのタスクマネージャー、Oracle Enterprise Manager、またはオペレーティングシステムを介して、環境変数(LinuxのOracle_Sid)を確認してください。

Linuxは実際に何に適していますか? Linuxは実際に何に適していますか? Apr 12, 2025 am 12:20 AM

Linuxは、サーバー、開発環境、埋め込みシステムに適しています。 1.サーバーオペレーティングシステムとして、Linuxは安定して効率的であり、多くの場合、高電流アプリケーションの展開に使用されます。 2。開発環境として、Linuxは効率的なコマンドラインツールとパッケージ管理システムを提供して、開発効率を向上させます。 3.埋め込まれたシステムでは、Linuxは軽量でカスタマイズ可能で、リソースが限られている環境に適しています。

Redisが正常に開始されるかどうかを確認する方法 Redisが正常に開始されるかどうかを確認する方法 Apr 10, 2025 pm 06:42 PM

Redisが正常に開始されるかどうかを確認する方法は次のとおりです。端子コマンドを介してプロセスステータスを確認してください:Linux/Unix:PS -EF | grep redis:ps aux | Grep RedisはRedisクライアントツールとPingを介して接続します:Redis-Cli Ping Access Redis Webインターフェイス:http:// [Redis Instance IP]:6379/info

RedisでLinuxを開始する方法 RedisでLinuxを開始する方法 Apr 10, 2025 pm 08:00 PM

LinuxシステムでRedisを起動するには:Redisパッケージをインストールします。 Redisサービスを有効にして開始します。 Redisが実行されていることを確認します。 Redisサーバーに接続します。高度なオプション:Redisサーバーを構成します。パスワードを設定します。 SystemDユニットファイルを使用します。

Redisログを表示する方法 Redisログを表示する方法 Apr 10, 2025 pm 04:27 PM

Redisログファイルは、通常/log/log/redis/redis-server.logまたは/&lt; redisインストールディレクトリ&gt; \ redis.logにあります。 Tail -F Redis -Server.logなどのコマンドラインツールを使用してログを表示できます。デフォルトのログレベルは「verbose」です。これは、redis.conf構成ファイルを変更することで変更できます。 Redisはログ分離もサポートしており、複数のファイルへのログを可能にします。

See all articles