[オペレーティング システム] Linux プログラミング - マルチスレッドの作成と使用

        この記事は合計 2820 文字で構成されており、推定読了時間は 6 分です。

目次

プロセスの欠陥

スレッドとプロセスの違い

スレッドの作成と実行のプロセス

pthread_create

pthread_join


プロセスの欠陥

        各プロセスには独立したメモリ空間があるため、プロセスの作成とコピーはオペレーティング システムに大きな負担をもたらします。プロセスの作成プロセス間データ交換に加えて、これらのオーバーヘッドは「コンテキスト スイッチ」からも発生し、「コンテキスト スイッチ」が最も多くのリソースを消費します。

        コンテキストスイッチング、その実際の意味はタスクスイッチング、または CPU レジスタスイッチングですマルチタスク カーネルは、別のタスクを実行することを決定すると、実行中のタスクの現在の状態、つまり CPU レジスタの内容全体を保存します。これらの内容はタスク自身のスタックに保存され、スタックへのプッシュが完了すると、次に実行するタスクの現在のステータスがタスクのスタックからCPUレジスタに再ロードされ、次のタスクの実行が開始されます。 「コンテキストスイッチング」。

        プロセスの利点を維持し、プロセスによってもたらされるスペースとデータ転送の時間消費を改善するために、人々は「スレッド」を設計しました。

        スレッドを作成すると、プロセスによるさまざまな影響が軽減され、スレッドにはプロセスと比較して次のような利点があります。

  • スレッドの作成とコンテキストの切り替えは、プロセスの作成とコンテキストの切り替えよりも高速です。
  • スレッド間でデータを交換するときに追加のテクノロジーを導入する必要はありません。

スレッドとプロセスの違い

        各プロセスのメモリ空間は、グローバル変数を格納するデータ領域ヒープ(動的割り当て関数mallocなどで提供される領域)、スタック(関数の実行時に生成される)で構成されます各プロセスにはこの独立した空間があり、そのモデルは次の図に示されています。 

        スレッドはプロセス設計モデルに基づき、複数のコードの実行フローをデータ領域とヒープに集中させ、スタック領域を分離し、以下のようなモデル構造を設計しています。

        複数のスレッドがデータ領域とヒープを共有できるようになります。

        したがって、プロセスとスレッドの定義は次のように大まかに要約できます。

  • プロセス: オペレーティング システムにおけるリソース割り当ての最小単位およびスレッドのコンテナー
  • スレッド: プロセス内で個別の実行フローを構成する単位であり、CPU スケジューリングの基本単位です。

スレッドの作成と実行のプロセス

pthread_create

        スレッドには個別の実行フローがあるため、専用の main 関数を設計する必要があり、実行フロー内の関数の実行をオペレーティング システムに要求する必要があります。

        スレッドを作成するには、次のヘッダー ファイルと関数を参照できます。

#include <pthread.h>

int pthread_create (pthread_t * restrict thread , const pthread_attr_t * restrict attr , void * (* start_routine)(void * ), void * restrict arg);

// 成功时返回 0, 失败时返回其他值。

/* 参数含义

   thread: 保存新创建线程 ID 的变量地址值。线程与进程相同,也需要用于区分不同线程的ID。
   attr: 用于传递线程属性的参数,传递 NULL时,创建默认属性的线程。
   start routïne: 相当于线程 main 函数的、在单独执行流中执行的函数地址值(函数指针) 。
   arg: 通过第三个参数传递调用函数时包含传递参数信息的变量地址值。
*/

例:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *thread_func1(void *arg);

int main(int argc, char *argv[])
{
    pthread_t t_id; // 线程的id,类比与进程号
    int thread_cycle = 10;

    if (pthread_create(&t_id, NULL, thread_func1, (void *)&thread_cycle /*注意要强转*/) != 0)
    {
        puts("pthread creation error");
        return -1;
    };
    sleep(11); //让Main函数停留 11 s,一般而言,等待事件设置的都比理论上的多1-2s,以保证能够执行完整
    puts("thread execution end");
    return 0;
}

void *thread_func1(void *arg)
{
    int i;
    int cnt = *((int *)arg); // 将指向void类型的指针类型转换为指向int类型的指针,然后再对其进行取值
    for (i = 0; i < cnt; i++)
    {
        sleep(1);
        printf("cycle: %d \n", i);
    }
    return NULL;
}

操作結果:

        上記のコードの main 関数のスリープをスレッドの理論上の合計実行時間よりも低い値に変更すると、実行フローが不完全になります。

        ただし、睡眠時間は客観的な時間、つまりリアルタイムに基づいて推定される時間です。コンピュータがスレッド内で関数を実行する場合、必ずしもこの時間に従うとは限りません。スレッド実行フローのタイム スライスをより正確に制御する方法はあるのでしょうか?

pthread_join

pthread_join関数        を使用してみましょう。その構造と参照ヘッダー ファイルは次のとおりです。

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

//成功时返回 0 ,失败时返回其他值。

/* 参数含义
   
   thread:欲操作的线程,以该线程的id传入。
   status:保存该线程 main 函数所返回的指针变量地址值 。

*/

例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

void* thread_func2(void *arg);

int main(int argc, char *argv[]) 
{
	pthread_t t_id;
	int thread_cycle= 5;
	void * thr_ret;
	
	if(pthread_create(&t_id, NULL, thread_func2, (void*)&thread_cycle)!=0)
	{
		puts("pthread creation error");
		return -1;
	}; 	
    //等待线程t_id的执行完毕。完毕后该线程返回的数据将存放在thr_ret中
	if(pthread_join(t_id, &thr_ret)!=0) 
	{
		puts("pthread_join error");
		return -1;
	};
    //将线程执行完后的返回值输出。执行这条指令时线程已结束。
	printf("Thread return message: %s \n", (char*)thr_ret); 
	free(thr_ret);
	return 0;
}

void* thread_func2(void *arg) 
{
	int i;
	int cnt=*((int*)arg);
	char * msg=(char *)malloc(sizeof(char)*20);
	strcpy(msg, "Hello, I’m thread but now I am dead and buried. \n"); 

	for(i=0; i<cnt; i++)
	{
		sleep(1);  
        printf("cycle: %d \n",i);
	}

	return (void*)msg; //这是
}

操作結果:

        ループが終了すると、つまり理論上は 5 秒後、スレッドは終了し、値を返します。このとき、変数はpthread_join関数の後に正しく出力されます。明らかに、この方法は手動による睡眠推定よりも正確に時間を制御できます。

おすすめ

転載: blog.csdn.net/weixin_42839065/article/details/131523606