Linux系统编程:线程创建、回收、分离

线程创建、回收、分离

一、线程的概念

1.1线程与进程的区别

进程:分配资源(内存)的最小单位,有独立的 进程地址空间,有独立的pcb。

线程:程序执行的最小单位,没有独立的进程地址空间,有独立的pcb。每一个进程由一个或 者多个线程组成,即每个进程至少有一个主线程。

1.2线程之间的资源共享

独享:栈空间(内核栈、用户栈)
共享 ./text ./data ./rodata ./bsss heap 全局变量

1.3线程id

与进程相似,每个线程都有自己的id,获取线程id函数如下:

pthread_t pthread_self(void);

在头文件
返回值:本线程id,其类型为pthread_t

二、线程的创建

2.1创建函数

int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*start_rountn)(void *), void *arg);

pthread_t *tid:传出参数,新创建的线程 id
const pthread_attr_t *attr:线程属性。可以设置,默认为NULL。
void *(*start_rountn)(void *):子线程回调函数。线程创建成功,该函数会被自动调用。
void *arg:回调函数的参数。没有的话,传NULL。
返回值:成功:0 ,失败:errno。

2.2创建一个线程

#include
#include
#include
#include
#include

void *thread_fun(void *argc){    //回调函数,子线程执行的函数
	printf("new tread\n");
	return NULL;
}

int main(int argc,char*argv[]){
	int ret;
	pthread_t tid;
	
	ret = pthread_create(&tid,NULL,thread_fun,NULL);  //创建子线程
	if(ret ! = 0){
		printf("create failed\n");
		exit(1);
	}
	sleep(1);   //等待子线程运行完
	printf("mian thread\n");
	
	return 0;
}

输出
在这里插入图片描述

2.3创建多个线程

#include
#include
#include
#include
#include

void *thread_fun(void *argc){
	int th = (int)argc;
	sleep(th);   //子线程依次执行
	printf("--I am thread %dth, pid=%d, tid=%lu\n",th+1,getpid(),pthread_self()); //打印进程id,线程id
	return NULL;
}

int main(int argc,char*argv[]){
	int i;
	int ret;
	pthread_t tid;

	for(i = 0; i < 5;i++){     //循环创建线程
		ret = pthread_create(&tid,NULL,thread_fun,(void*)i);
		if(ret != 0){
			printf("create failed\n");
			exit(1);
		}
	}
	
	sleep(i);  //等待所有子线程执行完
	printf("main: I am main , pid=%d,tid=%lu\n",getpid(),pthread_self());  //打印进程id,主线程id

	return 0;
}

在这里插入图片描述
从结果看出,进程id一样,线程id不同。

三、线程回收

3.1概述

主线程生成并执行子线程,主线程可能将于子线程之前结束,但是如果主线程需要用到子线程的处理结果,即主线程需要等待子线程执行完成之后再结束,这个时候就要用到pthread_join()方法了。

pthread_join()有两种作用:
1.用于等待其他线程结束:当调用 pthread_join() 时,当前线程会处于阻塞状态,直到被调用的线程结束后,当前线程才会重新开始执行。

2.对线程的资源进行回收:如果没有对线程使 pthread_join() 的话,该线程结束后并不会释放其内存空间,这会导致该线程变成了“僵尸线程”。分离属性线程除外。

3.2函数原型

int pthread_join(pthread_t thread, void **retval);

thread: 待回收的线程id
retval:传出参数。 回收的那个线程的退出值。
返回值:成功:0,失败:errno

3.3 主线程等待子线程

void *thread_fun(void *argc){    //回调函数,子线程执行的函数
	sleep(5);
	printf("new thread sleep 5s\n");
	return NULL;
}

int main(int argc,char*argv[]){
	int ret;
	pthread_t tid;
	
	ret = pthread_create(&tid,NULL,thread_fun,NULL);  //创建子线程
	if(ret != 0) {
		printf("create failed\n");
	}

	pthread_join(tid,NULL);   //主线程阻塞
	printf("main thread\n");
	
	return 0;
}

运行结果:
在这里插入图片描述
当主线程运行到pthread_join(tid,NULL),便会阻塞在这里,等待子线程回调函数执行完,然后主线程继续下执行。

3.4接受子线程的返回值

typedef struct {
    int id;
    int age;;
    char name[64];
}stu;


void *thread_fun(void *arg)
{
 
    stu * retvar = (stu *)malloc(sizeof(exit_t));
    retvar->id=1;
    retvar-> age= 16;
    strcpy(retvar->name,"uzi");
    //子线程退出
    return (void *)retvar;

}

int main(void)
{
    pthread_t tid;
    int ret;
    stu * retval;
    
    ret = pthread_create(&tid,NULL,thrd_func,NULL);

    if(ret != 0){
        printf("create failed\n");
        exit(1);
    }
    
    pthread_join(tid,(void **)&retval);   //接受回调函数的返回值
    printf("子线程返回值为\n");
    printf("id = %d ,age = %d,name = %s\n",retval->id,retval->age,retval->name);

    return 0;
}                                              

运行结果:
在这里插入图片描述

3.5接受多个线程的返回值

int val = 0;

void *thread_fun(void *arg){
	
	int i;
	i = (int)arg;
	sleep(i);
	switch(i){
		case 0 :
			val = 100;
			return (void*)val;
		//	break;
		case 1 :	
			val = 200;
			return (void*)val;
		//	break;
		case 2 :
			val = 300;
			return (void*)val;
		//	break;
		case 3 :
			val = 400;
			return (void*)val;
		//	break;
		default :	
			val = 500;
			return (void*)val;
		//	break;
	}
	return NULL;

}

int main(void)
{
    pthread_t tid[5];  //保存线程id
    int ret;
	int i;
    int * retval[5];    //存储接受的返回值
  
    for(i = 0;i < 5;i++){
		ret = pthread_create(&tid[i],NULL,thread_fun,(void*)i);  //给线程传参i

		 if(ret != 0){
			printf("create failed\n");
			exit(1);
		 }
	}
	for(i = 0;i < 5 ;i++){
		pthread_join(tid[i],(void **)&retval[i]);   //接受回调函数的返回值i
		 printf("I am thread %dth,val=%d,tip=%lu\n",i+1,val,pthread_self());
	}
	 printf("I am main val=%d,tip=%lu\n",val,pthread_self());
    return 0;
}                                              

结果:
Linux系统编程:线程创建、回收、分离_第1张图片
从结果可以看出,各个线程是共享全局变量的。

3.6关于返回值的总结

参考https://blog.csdn.net/liuyuchen282828/article/details/100345308

调用函数的线程将挂起等待,直到 id 为 thread 的线程终止。 thread 线程以不同的方法终止,通过 pthread_join 得到的终止状态是不同的,总结如下:

1.如果 thread 线程通过 return 返回,retval 所指向的单元里存放的是 thread 线程函数的返回值,此时进程会返回到调用者,不会结束。

2.如果 thread 线程被别的线程调用 pthread_cancel 异常终止掉, retval 所指向的单元里存放的是常数PTHREAD_CANCELED。

3.如果 thread 线程是自己调用 pthread_exit 终止的,retval 所指向的单元存放的是传给 pthread_exit 的参数。

4.如果对 thread 线程的终止状态不感兴趣,可以传 NULL 给 retval 参数。

四、线程分离

线程区别于进程,线程可以将自己与主线程分离开。可以用函数形式分离,也可以通过在创建线程时,将其属性设置为分离属性。

4.1函数形式

函数的形式很简单

int pthread_detach(pthread_t thread);	

thread: 待分离的线程id
返回值:成功:0,失败:errno

4.2函数的运用

主线程来获取子线程的返回值

void *thread_fun(void *arg){
    int n=3;
    while(n--){
        printf("thread count %d\n",n);
        sleep(1);
    }   

    return (void *)1;
}

int main(void){
    pthread_t tid;
    void *tret;
    int err;
    
    pthread_create(&tid,NULL,thread_fun,NULL);
    pthread_detach(tid);        //线程分离   
    
    while(1){
        err = pthread_join(tid,&tret);   //获取返回值
        printf("---------err= %d\n",err);
        if(err != 0)
            fprintf(stderr,"thread error: %s\n",strerror(err));              
        else
            fprintf(stderr,"thread exit code %d\n",(int)tret);

        sleep(1);
    } 

结果:
Linux系统编程:线程创建、回收、分离_第2张图片
由于子线程与主线程分离,主线程不能再获取返回值,则会发生错误。

你可能感兴趣的:(并发编程,操作系统,多线程,linux)