ホームページ  >  記事  >  運用・保守  >  Linux システム プログラミングについての深い理解 - (pthread) スレッドの作成と使用

Linux システム プログラミングについての深い理解 - (pthread) スレッドの作成と使用

WBOY
WBOY転載
2022-02-02 07:00:312588ブラウズ

この記事では、Linux でのスレッドの作成と使用についての知識を提供します。お役に立てば幸いです。

Linux システム プログラミングについての深い理解 - (pthread) スレッドの作成と使用

1. はじめに

スレッドとプロセスの違い (1) プロセス: オペレーティング システムのスケジューリングの最小単位です。 Linux では、ps や top などのコマンドを使用してプロセスの詳細情報を表示できます。 (2) スレッド: プロセスのスケジューリングの最小単位であり、各プロセスにはメインスレッドがあります。プロセス内で主に実行されるのはスレッドです。

(3) プロセスIDはシステム全体で一意の識別子であり、プロセスの管理はPIDによって実現されます。プロセスが作成されるたびに、カーネルはプロセスに関するすべての情報を保存する構造を作成します。プロセス情報を保存する各ノードには、独自の PID も保存されます。プロセスを管理する必要がある場合、この ID を使用してプロセスを実行できます (シグナルの送信など)。子プロセスが終了し、リサイクルする必要がある場合 (子プロセスが exit() を呼び出して終了するか、コードが実行される場合)、それは wait() システム コールを通じて実行する必要があります。リサイクルされないデッド プロセスは、ゾンビプロセスとなり、そのプロセス実体は存在しなくなりましたが、PID リソースを占有するため、リサイクルが必要になります。

スレッドの場合、アクティブに終了したい場合は、pthread_exit() を呼び出す必要があり、メインスレッドは pthread_join() を呼び出してリサイクルする必要があります (スレッドが「切り離し属性」を設定していない場合)。 。スレッドと同様にスレッド信号を送信すると、スレッド ID を介したプロセス間通信も実装されます。 A. 共有メモリ B. メッセージ キュー C. セマフォ D. 名前付きパイプ E. 無名パイプ F. シグナル G. ファイル H. ソケット スレッド間の通信方法: A.ミューテックス B. スピン ロック C. 条件変数 D. 読み取り/書き込みロック E. スレッド信号 F. グローバル変数

プロセス間で使用される通信方法では、カーネル コンテキストを切り替える必要があります。ペリフェラル (名前付きパイプ) でアクセスするか、 、ファイル)。そのため速度は遅くなります。スレッドが独自の通信方式を採用している場合、基本的には独自のプロセス空間で完結し、切り替えがないため通信速度が速くなります。つまり、種類の違いだけでなく、プロセスやスレッド間の通信方式にも速度が異なります。

注: マルチスレッドを実行しているプロセスがシグナルをキャッチすると、メインスレッドのみがブロックされ、他の子スレッドは影響を受けることなく実行を継続します。

2. スレッド関連関数の概要

2.1 スレッドの作成

pthread_create は、Unix オペレーティング システム (Unix、Linux) でスレッドを作成するための関数です。 、など)。コンパイル時にリンク ライブラリを指定する必要があります。 -lpthread 関数プロトタイプ

#include <pthread.h>
int pthread_create
(
pthread_t *thread, 
const pthread_attr_t *attr,
void *(*start_routine) (void *), 
void *arg
);

パラメータの紹介

最初のパラメータは、スレッド識別子へのポインタです。 2 番目のパラメータは、スレッドのプロパティを設定するために使用されます。デフォルトでは NULL を入力できます。 3 番目のパラメータは、関数を実行するスレッドの開始アドレスです。最後のパラメータは、関数を実行するためのパラメータです。パラメータは必要なく、NULL を入力できます。 Linux で関数ヘルプを表示します。 # man pthread_create

Linux システム プログラミングについての深い理解 - (pthread) スレッドの作成と使用 戻り値: スレッドが正常に作成された場合は、0 が返されます。スレッドの作成に失敗した場合は、エラー番号が返されます。スレッドが正常に作成された後、attr パラメーターを使用してさまざまなスレッド属性を指定します。新しく作成されたスレッドは、start_rtn 関数のアドレスから実行を開始します。この関数には、ユニバーサル ポインタ パラメータ arg が 1 つだけあります。スレッド動作関数に複数のパラメータを渡す必要がある場合は、これらのパラメータを構造体に入れる必要があります。次に、この構造体のアドレスがパラメータ arg として渡されます。

例:

#include <stdio.h>
#include <pthread.h>
//线程函数1
void *pthread_func1(void *arg)
{
    while(1)
    {
        printf("线程函数1正在运行.....\n");
        sleep(2);
    }
}
//线程函数2
void *pthread_func2(void *arg)
{
    while(1)
    {
        printf("线程函数2正在运行.....\n");
        sleep(2);
    }
}
int main(int argc,char **argv)
{
    
    pthread_t thread_id1;
    pthread_t thread_id2;
   /*1. 创建线程1*/
    if(pthread_create(&thread_id1,NULL,pthread_func1,NULL))
    {
        printf("线程1创建失败!\n");
        return -1;
    }
    /*2. 创建线程2*/
    if(pthread_create(&thread_id2,NULL,pthread_func2,NULL))
    {
        printf("线程2创建失败!\n");
        return -1;
    }
    
    /*3. 等待线程结束,释放线程的资源*/
    pthread_join(thread_id1,NULL);
    pthread_join(thread_id2,NULL);
    return 0;
}
//gcc pthread_demo_code.c -lpthread

2.2 スレッドの終了

プロセスが次のときに exit 関数を呼び出すのと同じように、スレッドは pthread_exit 関数を呼び出して実行を終了します。それは終わります。この関数の機能は、それを呼び出したスレッドを終了し、オブジェクトへのポインタを返すことです。

この関数の機能は、呼び出し元のスレッドを終了し、オブジェクトへのポインタを返すことです。戻り値は、pthread_join 関数の 2 番目のパラメータを通じて取得できます。

関数プロトタイプ

#include <pthread.h>
void pthread_exit(void *retval);

パラメータ分析 スレッドが返す必要があるアドレス。注: スレッド スタックは、スレッドの終了時に解放される必要があります。つまり、スレッド関数は pthread_exit() を呼び出して終了する必要があります。そうしないと、メイン プロセス関数が終了するまで解放されません。

2.3 待機します。スレッドが終了するまで

pthread_join() 関数は、スレッドで指定されたスレッドがブロック方式で終了するのを待ちます。関数が戻ると、待機中のスレッドのリソースが再利用されます。スレッドが終了した場合、関数はすぐに戻ります。また、threadで指定するスレッドは結合可能(結合属性)属性である必要があります。関数のプロトタイプ

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

パラメータ 最初のパラメータ: スレッド識別子、つまりスレッド ID は、一意のスレッドを識別します。最後のパラメータ: 待機中のスレッドによって返されたアドレスを格納するために使用されるユーザー定義のポインター。戻り値 0 は成功を表します。失敗した場合は、エラー番号が返されます。スレッド戻り値の受け取り例:

//退出线程
pthread_exit ("线程已正常退出");
//接收线程的返回值
void *pth_join_ret1;
pthread_join( thread1, &pth_join_ret1);

2.4 スレッド分離属性

创建一个线程默认的状态是joinable(结合属性),如果一个线程结束运行但没有调用pthread_join,则它的状态类似于进程中的Zombie Process(僵死进程),即还有一部分资源没有被回收(退出状态码),所以创建线程者应该pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于进程的wait,waitpid)。但是调用pthread_join(pthread_id)函数后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。

pthread_detach函数可以将该线程的状态设置为detached(分离状态),则该线程运行结束后会自动释放所有资源。 函数原型

#include <pthread.h>
int pthread_detach(pthread_t thread);

参数 线程标识符 返回值 0表示成功。错误返回错误码。 EINVAL线程并不是一个可接合线程。 ESRCH没有线程ID可以被发现。

2.5 获取当前线程的标识符

pthread_self函数功能是获得线程自身的ID。 函数原型

#include <pthread.h>
pthread_t pthread_self(void);

返回值 当前线程的标识符。 pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则显示结果出问题。

2.6 自动清理线程资源

线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序。用于程序异常退出的时候做一些善后的资源清理。 在POSIX线程API中提供了一个pthread_cleanup_push()/pthread_cleanup_pop()函数用于自动释放资源。从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用 pthread_exit()和异常终止)都将执行pthread_cleanup_push()所指定的清理函数。

注意:pthread_cleanup_push函数与pthread_cleanup_pop函数需要成对调用。 函数原型

void pthread_cleanup_push(void (*routine)(void *),void *arg); //注册清理函数
void pthread_cleanup_pop(int execute); //释放清理函数

参数 void (*routine)(void *) :处理程序的函数入口。 void *arg :传递给处理函数的形参。 int execute:执行的状态值。 0表示不调用清理函数。1表示调用清理函数。

导致清理函数调用的条件:

调用pthread_exit()函数

pthread_cleanup_pop的形参为1。 注意:return不会导致清理函数调用。

2.7 自动清理线程示例代码

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
//线程清理函数
void routine_func(void *arg)
{
   printf("线程资源清理成功\n");
}
//线程工作函数
void *start_routine(void *dev)
{
   pthread_cleanup_push(routine_func,NULL);
   //终止线程
   // pthread_exit(NULL);
    
   pthread_cleanup_pop(1); //1会导致清理函数被调用。0不会调用。
}
int main(int argc,char *argv[])
{
   pthread_t thread_id;  //存放线程的标识符
   /*1. 创建线程*/
   if(pthread_create(&thread_id,NULL,start_routine,NULL)!=0)
   {
      printf("线程创建失败!\n");
   } 
  /*2.设置线程的分离属性*/
   if(pthread_detach(thread_id)!=0)
   {
   printf("分离属性设置失败!\n");
   }
   while(1){}
   return 0;
}

2.8 线程取消函数

pthread_cancel函数为线程取消函数,用来取消同一进程中的其他线程。

头文件: #include <pthread.h>
函数原型:pthread_cancel(pthread_t tid);

相关推荐:《Linux视频教程

以上がLinux システム プログラミングについての深い理解 - (pthread) スレッドの作成と使用の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.imで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。