线程(创建,传参,接合,退出)

本文介绍了Linux环境下的线程概念,包括线程作为轻量级进程的特性,如何使用pthread_create创建线程,以及如何通过函数参数传递不同类型的数据。同时,文章讨论了线程间的通信方式,如参数传递、pthread_exit和pthread_join的使用,以及线程的退出和资源释放。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1、线程

2、创建线程

3、线程传参

4、子线程与主线程通信方式

5、线程接合与退出

1、线程

线程实际上是应用层的概念,在Linux内核中,所有的调度实体都被称为任务(task),他们之间的区别是:有些任务自己拥有一套完整的资源,而有些任务彼此之间共享一套资源,如下图所示。

上图中:

  • 左边是一个含有单个线程的进程,它拥有自己的一套完整的资源。
  • 右边是一个含有两条线程的进程,线程彼此间共享进程内的资源。

由此可见,线程是一种轻量级进程,提供一种高效的任务处理方式

2、创建线程

创建线程的函数接口说明如下:

创建一个子线程?  -> pthread_create()  -> man 3 pthread_create
pthread_t tid1;
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                      void *(*start_routine) (void *), void *arg);
函数参数:thread:储存线程ID号的变量的地址          pthread_t -> 线程ID号的数据类型
        attr:线程的属性,普通属性填NULL
        start_routine:线程的执行函数。该线程需要做什么任务,就写到这个函数中函数必须是:void *fun(void *arg) 回调函数-->函数指针
        arg:传递给子线程的参数(任意指针类型)
返回值:成功:0
        失败:非0错误码
 
 Compile and link with -pthread. 只要你的程序设计到线程的函数,在编译时必须链接线程库
编译命令: gcc xxx.c -o xxx -lpthread(编译线程的时候一定要加线程库)

理论上按64位系统的虚拟内存大小,理论上可以创建无数个线程。
事实上,肯定创建不了那么多线程,除了虚拟内存的限制,还有系统的限制。

3、线程传参

利用pthread_create函数的第四个参数来传参

1)int类型数据 先将arg强制转换为int*类型,再解引用

2)char类型数据 先类型转换,再解引用

3)char*(字符串)类型数据 直接类型转换,不需要解引用

4)指针类型数据 直接类型转换,不需要解引用

5)结构体两种方式的数据 结构体对象需要类型转换和解引用,结构体指针只需要类型转换,不需要解引用

线程传参的测试代码如下::

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

struct student
{
	char name[20];
	char sex;
	int height;
	float scores;
};

//1、int
void* fun1(void* arg)
{
	int a = *(int*)arg;
	while(1)
	{
		printf("[%d]%s 传参为a = %d\n",__LINE__,__FUNCTION__,a);
		sleep(1);
	}
}

//2、char
void* fun2(void* arg)
{
	char c = *(char *)arg;
	while(1)
	{
		printf("[%d]%s 传参为c = %c\n",__LINE__,__FUNCTION__,c);
		sleep(1);
	}
}

//3、char*
void* fun3(void* arg)
{
	while(1)
	{
		printf("[%d]%s 传参为s = %s\n",__LINE__,__FUNCTION__,(char *)arg);
		sleep(1);
	}
}

//4、指针
void* fun4(void* arg)
{
	int* p = (int *)arg;
	while(1)
	{
		printf("[%d]%s 传参为p = %d\n",__LINE__,__FUNCTION__,*p);
		sleep(1);
	}
}

//5、结构体对象
void* fun5(void* arg)
{
	struct student s1 = *(struct student*)arg;
	while(1)
	{
		printf("[%d]%s 传参为s1 = %s %c %d %f\n",__LINE__,__FUNCTION__,s1.name,s1.sex,s1.height,s1.scores);
		sleep(1);
	}
}

//6、结构体指针
void* fun6(void* arg)
{
	struct student* s2 = (struct student*)arg;
	while(1)
	{
		printf("[%d]%s 传参为s2 = %s %c %d %f\n",__LINE__,__FUNCTION__,s2->name,s2->sex,s2->height,s2->scores);
		sleep(1);
	}
}

int main()
{
	int a = 250;
	char c = 'a';
	char* s = "hello world";
	int* p = &a;
	struct student s1 = { "sakura0908",'b',180,90.5};
	struct student* s2 = &s1;
	int ret = 0;
	
	pthread_t tid1;
	ret = pthread_create(&tid1,NULL,fun1,(void*)&a);
	if(ret != 0)
	{
		printf("pthread_create1 fail\n");
		return -1;
	}
	//printf("pthread_create1 ok\n");
	
	pthread_t tid2;
	ret = pthread_create(&tid2,NULL,fun2,(void*)&c);
	if(ret != 0)
	{
		printf("pthread_create2 fail\n");
		return -1;
	}
	//printf("pthread_create2 ok\n");
	
	pthread_t tid3;
	ret = pthread_create(&tid3,NULL,fun3,s);
	if(ret != 0)
	{
		printf("pthread_create3 fail\n");
		return -1;
	}
	//printf("pthread_create3 ok\n");
	
	pthread_t tid4;
	ret = pthread_create(&tid4,NULL,fun4,p);
	if(ret != 0)
	{
		printf("pthread_create4 fail\n");
		return -1;
	}
	//printf("pthread_create4 ok\n");
	
	pthread_t tid5;
	ret = pthread_create(&tid5,NULL,fun5,(void*)&s1);
	if(ret != 0)
	{
		printf("pthread_create5 fail\n");
		return -1;
	}
	//printf("pthread_create5 ok\n");
	
	pthread_t tid6;
	ret = pthread_create(&tid6,NULL,fun6,s2);
	if(ret != 0)
	{
		printf("pthread_create6 fail\n");
		return -1;
	}
	//printf("pthread_create6 ok\n");
	
	while(1)
	{
		//printf("[%d]%s\n",__LINE__,__FUNCTION__);
		sleep(1);
	}
	
	return 0;
}

4、子线程与主线程通信方式

主线程与子线程之间互相通信有哪几种方式:
1.主线程通过参数arg来给子线程发送数据(常用)
2.子线程用pthread_exit()退出,主线程用pthread_join()来接受子线程的值
3.全局变量(常用)

5、线程接合与退出

与进程类似,线程退出之后不会立即释放其所占有的系统资源,而会成为一个僵尸线程。其他线程可使用 pthread_join() 来释放僵尸线程的资源,并可获得其退出时返回的退出值。

包括主线程在内,所有线程的地位是平等的,任何线程都可以先退出,任何线程也可以接合另外一条线程。

接合与退出函数接口说明如下:

pthread_join()  -> man 3 pthread_join
功能:阻塞等待子线程的退出
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
参数:thread:需要接合的线程的ID号
      retval:储存子线程的退出值指针,如果填NULL,代表不关心子线程的退出状态。
返回值:成功:0
        失败:非0
接口说明:
若指定tid的线程尚未退出,那么该函数将持续阻塞。
若只想阻塞等待指定线程tid退出,而不想要其退出值,那么val可置为NULL。
若指定tid的线程处于分离状态,或不存在,则该函数会出错返回。


退出线程  -> pthread_exit()  -> man 3 pthread_exit
#include <pthread.h>
void pthread_exit(void *retval);
函数作用:子线程主动结束退出
参数:retval:子线程退出值变量的地址   -> 这个退出值必须是全局变量
说明:不能将遗言存放到线程的局部变量里,因为如果用户写的线程函数退出了,线程函数栈上的局部变量可能就不复存在了。

导致线程退出的因素:
 The new thread terminates in one of the following ways:
//以下这几种情况之一都可以导致线程退出
       * It calls pthread_exit(3), specifying an exit status value that is available to another thread in the  same  process  that  calls pthread_join(3).
//1)当线程调用了pthread_exit(),还可以将退出值返回给那个接合的它的线程。
       * It returns from start_routine().  This is equivalent to calling pthread_exit(3) with the value supplied in the return statement.
//2)当例程函数返回时,也可以导致线程的退出,return后面的值就是退出值。
       * It is canceled (see pthread_cancel(3)).
//3)收到取消请求。
       * Any  of the threads in the process calls exit(3), or the main thread performs a return from main().  This causes the termination of all threads in the process.
//4)任意一个线程调用exit(),都会导致所有的线程都退出/main函数返回

线程接合与退出的测试代码如下:

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

int ret = 250;

void* fun1(void* arg)
{
	int cnt = 5;
	while(cnt--)
	{
		printf("%d...	[%s][%d]\n",cnt,__FUNCTION__,__LINE__);
		sleep(1);
	}
	printf("等待主线程退出...\n");
	
	//pthread_exit("1");
	pthread_exit(&ret);
}

int main()
{
	int ret = 0;
	pthread_t tid1;
	ret = pthread_create(&tid1,NULL,fun1,NULL);
	if(ret != 0)
	{
		printf("pthread_create fail\n");
		return -1;
	}
	printf("pthread_create ok\n");
	
	int cnt = 7;
	while(cnt--)
	{
		printf("[%s][%d]	[%d]...\n",__FUNCTION__,__LINE__,cnt);
		sleep(1);
	}
	
	printf("阻塞等待子线程退出\n");
	void* retval = NULL;
	ret = pthread_join(tid1,&retval);
	if(ret != 0)
	{
		printf("pthread_join fail\n");
		return -1;
	}
	printf("pthread_join ok\n");
	
	//int ret_val = atoi((char *)retval); 
	int ret_val = *(int*)retval;
	printf("接收子线程的值:%d\n",ret_val); 
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值