Linux c ++マルチスレッドの問題

プロジェクトの問題により、最近スレッド関連の知識が含まれているため、全員の進捗状況をまとめてみましょう。

従来のUnixモデルでは、プロセスが別のエンティティによって実行される必要がある場合、プロセスは子プロセスをforkし、子プロセスに処理させます。Unixでのほとんどのネットワークサーバープログラムはこのように記述されています。つまり、親プロセスが接続を受け入れ、子プロセスを派生させ、子プロセスがクライアントとの対話を処理します。

このモデルは長年使用されてきましたが、フォークにはいくつかの問題があります。

  • フォークは高価です。メモリイメージは親プロセスから子プロセスにコピーする必要があり、すべての説明語は子プロセスなどにコピーする必要があります。現在、一部のUnix実装では、コピーオンライトと呼ばれるテクノロジーを使用しています。これにより、親プロセスのデータスペースから子プロセスへのコピーオンライトを回避できます。この最適化手法にもかかわらず、フォークはまだ高価です。
  • 2.子プロセスをフォークした後、プロセス間通信(IPC)を使用して親プロセスと子プロセスの間で情報を転送する必要があります。子プロセスには親プロセスのデータスペースのコピーと最初からすべての記述語があるため、Forkの前の情報は簡単に渡すことができます。ただし、子プロセスから親プロセスに情報を返すには、さらに多くの作業が必要です。

スレッドはこれら2つの問題の解決に役立ちます。スレッドは、プロセスよりも「軽量」であるため、軽量プロセスと呼ばれることもあります。一般的に、スレッドの作成は、プロセスの作成よりも10〜100倍高速です。

    プロセス内のすべてのスレッドは同じグローバルメモリを共有するため、スレッドは情報を簡単に共有できますが、この単純さは同期の問題も引き起こします。

プロセス内のすべてのスレッドは、グローバル変数を共有するだけでなく、プロセス命令、ほとんどのデータ、開いているファイル(説明語など)、シグナルハンドラーとシグナル処理、現在の作業ディレクトリ、ユーザーIDとグループIDも共有します。ただし、各スレッドには独自のスレッドID、レジスタセット(プログラムカウンターとスタックポインターを含む)、スタック(ローカル変数と戻りアドレスを格納するために使用)、エラー、シグナルマスク、および優先順位があります。Linuxでのスレッドプログラミングは、Posix.1標準に準拠しており、Pthreadsと呼ばれています。すべてのpthread関数はpthread_で始まります。それらを呼び出す前に、pthread.hヘッダーファイル、関数ライブラリlibpthread.aをインクルードする必要があります。

スレッド属性


  • 構造とヘッダーファイル

スレッド属性構造はpthread_attr_tで、ヘッダーファイル/usr/include/pthread.hで定義されています。初期化関数はpthread_attr_initであり、この関数はpthread_create関数の前に呼び出す必要があります。属性オブジェクトには主に、バインドするかどうか、分離するかどうか、スタックアドレス、スタックサイズ、優先度が含まれます。デフォルトの属性は、非バインド、非分離、デフォルトの1Mスタック、および親プロセスと同じ優先度です。

  • スレッドバインディング

スレッドバインディングステータスを設定する関数はpthread_attr_setscopeです。2つのパラメーターがあります。1つ目は属性構造体へのポインター、2つ目はバインディングタイプです。PTHREAD_SCOPE_SYSTEM(バインド済み)とPTHREAD_SCOPE_PROCESS(アンバウンド)。次のコードは、バインドされたスレッドを作成します。  

    軽量プロセス(LWP:Light Weight Process)。軽いプロセスは、ユーザー層とシステム層の間にあるカーネルスレッドとして理解できます。スレッドリソースの割り当てとシステムによるスレッドの制御は、ライトプロセスによって実現され、1つのライトプロセスが1つ以上のスレッドを制御できます。。     

  • スレッド分離

スレッドの切り離された状態によって、スレッドがどのように終了するかが決まります。元のスレッドは、作成されたスレッドの終了を待ちます。pthread_join()関数が戻る場合のみ、作成されたスレッドは終了したと見なされ、それ自体が占有していたシステムリソースを解放できます。切り離されたスレッドは、このようなものではなく、他のスレッドによって待機されることはありません。実行されると、スレッドは終了し、システムリソースをすぐに解放します。プログラマーは、必要に応じて適切な分離状態を選択する必要があります。スレッドの切り離し状態を設定する関数は、pthread_attr_setdetachstate(pthread_attr_t * attr、int detachstate)です。2番目のパラメーターは、PTHREAD_CREATE_DETACHED(分離スレッド)およびPTHREAD _CREATE_JOINABLE(非分離スレッド)です。

  • スレッドの優先順位

通常、優先度の設定を最初に取得してから変更しますが、スレッドの優先度を変更するには、作成するプロセスの種類に注意する必要があります。

SCHED_OTHERは、作成された通常のプロセスを表します。
SCHED_FIFO && SCHED_RRは、優先度を変更できる作成されたスレッドを表し、高優先度は低優先度より優先されます。 
前者では、高優先度の操作中に、低優先度を先取りすることはできず、高優先度がアクティブに終了するまでしか待機できません。同じ優先度の場合、最初に実行中のプロセスが常にCPUを占有し、最初に実行中のプロセスがアクティブに終了した後にのみ後続のプロセスを実行できます。タイムスライスを取得します。
後者の場合、高優先度は低優先度を先取りします。高優先度操作中、低優先度は先取りできず、高優先度がアクティブに終了するまでしか待機できません。同じ優先度のプロセスの場合、各プロセスは順番に一定期間実行されますスライス(約100ms)。

/*
 * ThreadTest.cpp
 *
 *  Created on: Jul 10, 2019
 *  Author: DY
 */
//#include "../core/Thread.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
using namespace std;
extern "C" void* Thread2 (void*);
extern "C" void* Thread1 (void*);
void* Thread1(void*)
{
    sleep(1);
    int i,j;
    int policy;
    struct sched_param param;
    pthread_getschedparam(pthread_self(),&policy,&param);
    if(policy == SCHED_OTHER)  printf("SCHED_OTHER\n");
    if(policy == SCHED_RR);    printf("SCHED_RR 1 \n");
    for(i=1;i<10;i++){
        for(j=1;j<500;j++){}
        printf("thread 1\n");}
}
void* Thread2(void*)
{
    sleep(1);
    int i,j;
    int policy;
    struct sched_param param;
    pthread_getschedparam(pthread_self(),&policy,&param);
    if(policy == SCHED_OTHER)  printf("SCHED_OTHER\n");
    if(policy == SCHED_RR);    printf("SCHED_RR\n");

    for(i=1;i<10;i++){
        for(j=1;j<500;j++){}
        printf("thread 2\n");}
}
int main(){
    pthread_t p1,p2;
    struct sched_param param;
    pthread_attr_t attr,attr1;

    //初始化
    pthread_attr_init(&attr);
    pthread_attr_init(&attr1);
    //设置线程模式
    pthread_attr_setschedpolicy(&attr,SCHED_RR);
    pthread_attr_setschedpolicy(&attr1,SCHED_RR);
    //获取线程属性并打印
    pthread_attr_getschedparam(&attr, &param);
    printf("%d\n",param.__sched_priority);
    pthread_attr_getschedparam(&attr1, &param);
    printf("%d\n",param.__sched_priority);

    //线程的优先级[1,99]值越大优先级越高
    param.sched_priority = 50;
    pthread_attr_setschedparam(&attr,&param);
    //该函数的意思为创建的进程优先级是否同父进程相同
    //pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED);
    //创建线程
    pthread_create(&p1,&attr,Thread1,NULL);
    pthread_attr_getschedparam(&attr, &param);
    printf("%d\n",param.__sched_priority);

    param.sched_priority = 20;
    pthread_attr_setschedparam(&attr1,&param);
    //pthread_attr_setinheritsched(&attr1,PTHREAD_EXPLICIT_SCHED);
    pthread_create(&p2,&attr1,Thread2,NULL);
    pthread_attr_getschedparam(&attr1, &param);
    printf("%d\n",param.__sched_priority);

    //等待线程退出并销毁
    pthread_join(p2,NULL);
    pthread_join(p1,NULL);
    pthread_attr_destroy(&attr);
    pthread_attr_destroy(&attr1);
    //pthread_exit(NULL);
    return 0;
}

プログラムの実行中に問題が発生しました。g++コンパイル中に次のエラーが報告されます:エラー: 'void(*)(void *)'から 'void *(*)(void *)' [-fpermissive]への変換が無効です。その理由は、C言語コンパイラでは、一般的なポインタを任意の型のポインタに暗黙的に変換できるためです。これは、C ++では許可されていません。その理由は、pthread_create()の3番目のパラメーターは関数をロードするためであり、この関数には、渡されて汎用ポインターを返すパラメーターがあります。
したがって、上記のエラーの解決策:1)スレッド関数はvoid thread(void *)として定義され、呼び出し場所は次のように記述されます:int ret = pthread_create(&id、&attr、(viod *)&thread、NULL); 2)スレッド関数void * thread1(void *)として定義され、呼び出し場所はint ret = pthead_create(&id、&attr、thread1、NULL)です。次にコンパイルします。


スレッドの作成

//创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。
//函数原型:
int pthread_create(pthread_t *restrict tidp,
                    const pthread_attr_t *restrict attr,    
                    void *(*start_rtn)(void),void *restrict arg);
//形式参数:
//pthread_t *restrict tidp 要创建的线程的线程id指针,创建成功后,指向的内存存放创建线程的id;
//const pthread_attr_t *restrict attr一个不透明的属性对象,具体见上文属性的创建;
//void* (start_rtn)(void)返回值是void类型的指针函数;线程函数的起始地址,线程一旦创建立马执行;
//void* restrict arg start_rtn的形参;
//返回值:若是成功建立线程返回0,否则返回错误的编号。  
int ret = pthread_create (thread, attr, start_routine, arg);

スレッドハング

函数原型:int  pthread_join(pthread_t thread、void ** value_ptr);

パラメータの説明は次のとおりです:スレッドの終了を待機しているスレッドのスレッド番号; value_ptr終了するスレッドの戻り値。この関数の関数は、現在のスレッドを中断し、実行を続行する前に別のスレッドが戻るのを待ちます。つまり、プログラムがこの場所まで実行されると、プログラムは最初に停止し、スレッドIDがthreadであるスレッドが戻るのを待ってから、プログラムが断続的に実行されます。

スレッドの終了

関数プロトタイプ:int pthread_exit(void * rval_pt)

パラメータの説明は次のとおりです。

ここではpthread_exit 、スレッドを明示的に終了するために使用されます。通常、pthread_exit()関数は、スレッドが作業の終了後に存在し続ける必要がない場合に呼び出されます。main()が作成したスレッドの前に終了し、pthread_exit()を介して終了した場合、他のスレッドは引き続き実行されます。それ以外の場合は、main()の最後で自動的に終了します。

 

おすすめ

転載: blog.csdn.net/Ding86341631/article/details/95244721
おすすめ