linux_线程基础函数-pthread_self函数-pthread_create函数-pthread_exit函数-pthread_join函数

接上一篇:linux_线程概念-内核线程实现原理-线程共享资源-线程优缺点

  今天来分享线程的代码函数了,主要是获得线程ID、创建线程函数、线程退出函数、等待线程结束函数,以及分享这些函数的例子,话不多说,开始上菜:

此博主在CSDN发布的文章目录:我的CSDN目录,作为博主在CSDN上发布的文章类型导读

1.pthread_self函数

函数作用:
  获取线程ID。其作用对应进程中 getpid() 函数。
头文件:
  #include <pthread.h>
函数原型:
  pthread_t pthread_self(void);
函数参数:
  无
返回值:
  成功:返回线程的ID号;
失败:无!
注意:
  ①g++/gcc编译的时候需要加上参数-lpthread,编译pthread动态库。
  ②不应使用全局变量 pthread_t tid,在子线程中通过pthread_create传出参数来获取线程ID,而应使用pthread_self。
  ③线程ID:pthread_t类型,可理解为:typedef unsigned long int pthread_t;本质:在Linux下为无符号整数(%lu),其他系统中可能是结构体实现
   ④线程ID是进程内部,识别标志。(两个进程间,线程ID允许相同)

2.pthread_create函数

函数作用:
  创建一个新线程。 其作用,对应进程中fork() 函数。
头文件:
  #include <pthread.h>
函数原型:
  int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
函数参数:
  thread:传出参数,保存系统为我们分配好的线程ID。
  attr:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
  start_routine:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束,即创建线程的回调函数。
  arg:线程主函数执行期间所使用的参数。
返回值:
  成功:0;
  失败:错误号,可通过strerror函数打印错误信息。

  在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。
  start_routine函数接收一个参数,是通过pthread_create的arg参数传递给它的,该参数的类型为void *,这个指针按什么类型解释由调用者自己定义。
  start_routine的返回值类型也是void *,这个指针的含义同样由调用者自己定义。
  start_routine返回时,这个线程就退出了,其它线程可以调用pthread_join得到start_routine的返回值,类似于父进程调用wait(2)得到子进程的退出状态,稍后详细介绍pthread_join。

  pthread_create成功返回后,新创建的线程的id被填写到thread参数所指向的内存单元。
  我们知道进程id的类型是pid_t,每个进程的id在整个系统中是唯一的,调用getpid(2)可以获得当前进程的id,是一个正整数值。线程id的类型是thread_t,它只在当前进程中保证是唯一的,在不同的系统中thread_t这个类型有不同的实现,它可能是一个整数值,也可能是一个结构体,也可能是一个地址,所以不能简单地当成整数用printf打印,调用pthread_self(3)可以获得当前线程的id。
  由于pthread_create的错误码不保存在errno中,因此不能直接用perror(3)打印错误信息,可以先用strerror(3)把错误码转换成错误信息再打印。

2.1.线程与共享

先抛出结论:线程间共享全局变量!

3.pthread_exit函数

函数作用:
  将单个线程退出。
头文件:
  #include <pthread.h>
函数原型:
  void pthread_exit(void *retval);
函数参数:
  retval:表示线程退出状态,通常传NULL。
返回值:
  无。
注意:
  线程中,禁止使用exit函数,会导致进程内所有线程全部退出。
  在不添加sleep控制输出顺序的情况下。pthread_create在循环中,几乎瞬间创建5个线程,但只有第1个线程有机会输出(或者第2个也有,也可能没有,取决于内核调度)如果第3个线程执行了exit,将整个进程退出了,所以全部线程退出了。
  所以,多线程环境中,应尽量少用,或者不使用exit函数,取而代之使用pthread_exit函数,将单个线程退出。任何线程里exit导致进程退出,其他线程未工作结束,主控线程退出时不能return或exit。
  另注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

线程调用return、pthread_exit、exit总结:
   return:返回到调用者那里去。
   pthread_exit():将调用该函数的线程
   exit: 将进程退出。

4.pthread_join函数

函数作用:
  阻塞等待线程退出,获取线程退出状态。
头文件:
  #include <pthread.h>
函数原型:
  int pthread_join(pthread_t thread, void **retval);
函数参数:
  thread:线程ID (【注意】:不是指针);
  retval:存储线程结束状态。
返回值:
  成功:0;
  失败:错误号,可通过strerror函数打印错误信息。
注意:
  调用该函数的线程将挂起等待,直到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参数。

5.各函数调用例子:

5.1.pthread_self函数-pthread_create函数-创建多线程

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
//通过命令:ps -Lf PID #查看PID进程下的线程
void *tfn(void *arg)
{
    
    
	int i;
	i = (int)arg;
	sleep(i);	 //通过i来区别每个线程
	printf("我是第%d个线程, 线程ID = %lu\n", i+1, pthread_self());
	return NULL;
}
int main(int argc, char *argv[])
{
    
    
	int n = 5, i;
	pthread_t tid;
    //创建5个进程
	for (i = 0; i < n; i++) 
	{
    
    
		pthread_create(&tid, NULL, tfn, (void *)i);
		//将i转换为指针,在tfn中再强转回整形。
	}
	sleep(n);//程序暂停5s
	printf("主线程ID == %lu\n", pthread_self());

	return 0;
}

5.2.strerror函数-检验系统最多能创建多少个线程

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
//线程回调函数
void *tfn(void *arg)
{
    
    
    while (1)
        sleep(1);
}
int main(void)
{
    
    
	pthread_t tid;
	int ret, count = 1;
	//循环一直创建,直至程序错误退出,检验你的系统最多创建多少个线程
	for (;;) {
    
    
		ret = pthread_create(&tid, NULL, tfn, NULL);
		if (ret != 0) 
		{
    
    
			printf("%s\n", strerror(ret));
			break;
		}
		printf("---------%d---------------\n", ++count);
	}
	return 0;
}

5.3.线程之间共享全局变量

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

int var = 100;
//线程回调函数
void *tfn(void *arg)
{
    
    
	var = 200;
	printf("线程回调函数...\n");
	return NULL;
}

int main(void)
{
    
    
	printf("改变之前:var = %d\n", var);//var=100
	pthread_t tid;
	pthread_create(&tid, NULL, tfn, NULL);//创建线程
	sleep(1);
	printf("改变之后:var = %d\n", var);//var=200
	return 0;
}

5.4.pthread_exit函数调用-结束线程

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
//线程回调函数
void *tfn(void *arg)
{
    
    
	int i;
	i = (int)arg; //强转。
	if (i == 2)
	{
    
    
		pthread_exit(NULL);//结束线程
	}
	sleep(i);	 //通过i来区别每个线程
	printf("我是第%d个线程, 线程ID = %lu\n", i+1, pthread_self());
	return NULL;
}
int main(int argc, char *argv[])
{
    
    
	int n = 5, i;
	pthread_t tid;
	for (i = 0; i < n; i++) 
	{
    
    
		pthread_create(&tid, NULL, tfn, (void *)i);//将i转换为指针,在tfn中再强转回整形。
	}
	sleep(n);
	printf("主控线程ID = %lu\n", pthread_self());
	return 0;
}

5.5.pthread_join函数调用-获取线程退出的返回值

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
//自定义返回值
typedef struct {
    
    
	int a;
	int b;
} exit_t;
//线程回调函数
void *tfn(void *arg)
{
    
    
	exit_t *ret;
	ret = malloc(sizeof(exit_t)); //分配内存
	//改变值
	ret->a = 100;
	ret->b = 300;
	pthread_exit((void *)ret);
}
int main(void)
{
    
    
	pthread_t tid;
	exit_t *retval = NULL;
	pthread_create(&tid, NULL, tfn, NULL);
	//调用pthread_join可以获取线程的退出状态
	pthread_join(tid, (void **)&retval); //等待线程退出
	printf("a = %d, b = %d \n", retval->a, retval->b);//打印线程退出的返回值
	free(retval);//释放内存
	retval = NULL;//置NULL
	return 0;
}

5.6.获取多线程的返回值

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int var = 100;
//线程回调函数
void *tfn(void *arg)
{
    
    
    int i;
    i = (int)arg;
    sleep(i);//线程暂定is
    if (i == 1) 
	{
    
    
        var = 333;
        printf("第%d个线程:var = %d\n", i+1,var);
        return (void *)var;
    } 
	else  if (i == 3) 
	{
    
    
        var = 777;
        printf("第%d个线程:var = %d\n", i+1, var);
        pthread_exit((void *)var);

} 
else  {
    
    
        printf("第%d个线程:var = %d\n", i+1, var);
        pthread_exit((void *)var);
    }

    return NULL;
}
int main(void)
{
    
    
    pthread_t tid[5];
    int i;
    int *ret[5];  
	//创建5个线程
    for (i = 0; i < 5; i++)
	{
    
    
        pthread_create(&tid[i], NULL, tfn, (void *)i);
	}
    for (i = 0; i < 5; i++) 
	{
    
    
		//通过pthread_join获取线程返回状态
        pthread_join(tid[i], (void **)&ret[i]);
        printf("第%d个线程返回:%d\n", i+1, (int)ret[i]);
    }
    printf("主线程ID = %lu\t var = %d\n", pthread_self(), var);
    sleep(i);
    return 0;
}

以上就是本次的分享了,希望对大家有所帮助,欢迎关注博主一起学习更多的新知识!

猜你喜欢

转载自blog.csdn.net/qq_44177918/article/details/130442584