LV.5 进程、线程和进程间通信(3)- 线程的创建和回收

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

目录

前言

1.线程的创建

1.1.编译错误分析:

2.线程间参数传递:(重点难点)

2.1 引用传递和值传递

2.2  引用传递和值传递的应用场景和区别

3.线程的回收:

3.1查看线程

3.2使用线程的分离:

作业

总结



前言


1.线程的创建

 #include  <pthread.h>

 int  pthread_create (pthread_t *thread, const pthread_attr_t *attr,

                                        void *(*routine)(void *), void *arg);

 成功返回0,失败时返回错误码

 thread 线程对象

 attr 线程属性,NULL代表默认属性

 routine 线程执行的函数

 arg 传递给routine的参数 ,参数是void * ,注意传递参数格式,

1.1.编译错误分析:

1.createP_t.c:14:36: warning: passing argument 3 of ‘pthread_create’ from incompatible pointer type [-Wincompatible-pointer-types]

 ret = pthread_create(&tid,NULL,testThread,NULL); ^

In file included from createP_t.c:1:0:

/usr/include/pthread.h:233:12: note: expected ‘void * (*)(void *)’ but argument is of type ‘int * (*)(char *)’

意义:表示pthread_create参数3的定义和实际代码不符合,期望的是void * (*)(void *) ,实际的代码是int * (*)(char *)

解决方法:改为pthread_create(&tid,NULL,(void*)testThread,NULL);

2.createP_t.c:(.text+0x4b):对‘pthread_create’未定义的引用

collect2: error: ld returned 1 exit status   --------这个链接错误,

表示pthread_create这个函数没有实现

解决方法:编译时候加 -lpthread

注意事项:

1. 主进程的退出,它创建的线程也会退出。

2.线程创建需要时间,如果主进程马上退出,那线程不能得到执行

获取线程的id

通过pthread_create函数的第一个参数;通过在线程里面调用pthread_self函数

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

int* testThread(char *arg){
printf("This is a thread test\n");
printf("进程id=%d,线程id=%lu\n",getpid(),pthread_self());
//return NULL;
pthread_exit(NULL);//线程退出,功能和return类似,但是也有不同的功能
printf("This is a thread test2\n");//不会输出
}

int main(int argc, const char *argv[])
{
	pthread_t tid;
	int ret;
	ret = pthread_create(&tid,NULL,(void*)testThread,NULL);
	printf("mian thread");
    //与子线程的输出结果一样
	printf("进程id=%d,线程id=%lu\n",getpid(),tid);
	sleep(1);
	return 0;
}

2.线程间参数传递:(重点难点)

2.1 引用传递和值传递

编译错误:

createP_t.c:8:34: warning: dereferencing ‘void *’ pointer

     printf("input arg=%d\n",(int)*arg);

createP_t.c:8:5: error: invalid use of void expression

     printf("input arg=%d\n",(int)*arg);

错误原因 void *类型指针不能直接用*取值(*arg),因为编译不知道数据类型。

解决方法:转换为指定的指针类型后再用*取值  比如:*(int *)arg

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

int* testThread(void *arg){
printf("This is a thread test\n");
//1.地址传递
printf("arg = %d\n",*(int*)arg);
//2.值传递
//printf("arg = %d\n",(int)arg);
pthread_exit(NULL);

}

int main(int argc, const char *argv[])
{
	pthread_t tid;
	int ret;
	int arg = 5;
    //1.地址传递
	ret = pthread_create(&tid,NULL,(void*)testThread,(void*)&arg);
    //2.值传递==》这里直接把值5当成地址传过去
    //ret = pthread_create(&tid,NULL,(void*)testThread,(void*)arg);
	printf("mian thread");
	sleep(1);
	return 0;
}
  1. 通过地址传递参数,注意类型的转换
  2. 值传递,这时候编译器会告警,需要程序员自己保证数据长度正确

2.2  引用传递和值传递的应用场景和区别

下面的循环创建子线程的程序表明: 引用传递需要配合sleep()使用,因为创建子线程很快,但是执行子线程需要时间,等执行子线程时,可能for循环已经结束,此时i = 5(还没清楚为什么是5而不是4)

每个子线程的参数都是 地址&i,所以导致最终结果一样。直接传递值,就可以避免这个问题

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

int* testThread(void *arg){
printf("This is a thread test\n");

printf("arg = %d\n",*(int*)arg);
//printf("arg = %d\n",(int)arg);

pthread_exit(NULL);

}

int main(int argc, const char *argv[])
{
	pthread_t tid[5];
	int ret;
	int arg = 5;
	int i;
	for(i = 0;i < 5;i++){	
	ret = pthread_create(&tid[i],NULL,(void*)testThread,(void*)&i);
    sleep(1);
//	ret = pthread_create(&tid[i],NULL,(void*)testThread,(void*)i);
	}
	printf("mian thread");
	sleep(1);
	return 0;
}

运行错误:

*** stack smashing detected ***: ./mthread_t terminated

已放弃 (核心已转储)

原因:栈被破坏了(数组越界)

3.线程的回收:

3.1查看线程

ps -eLf |grep xxx 必须是L

使用pthread_join 函数:

#include  <pthread.h>

 int  pthread_exit(void); //线程结束

int  pthread_join(pthread_t thread, void **retval); //线程回收

注意:pthread_join 是阻塞函数,如果回收的线程没有结束,则一直等待

编译错误:

pjoin.c:13:5: error: unknown type name ‘pthead_t’

     pthead_t tid;

错误类型:未知的类型pthead_t  

错误可能:1拼写错误,2对应的头文件没有包含

pjoin.c:18:12: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘void *’ [-Wformat=]

     printf("thread ret=%s\n",retv);

错误类型:参数不匹配,期望的是char * ,但参数retv是void *

解决:在参数前面加强制类型转换(char*)retv

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

int* testThread(void *arg){
printf("This is a thread test\n");
sleep(1);
pthread_exit("thread return");
}

int main(int argc, const char *argv[])
{
	pthread_t tid;
	int ret;
	void* retv;
	ret = pthread_create(&tid,NULL,(void*)testThread,NULL);
	//回收子线程--对应子线程结束前 保持阻塞状态
	pthread_join(tid,&retv);
	printf("tread ret=%s\n",(char*)retv);//返回 thread return 
	printf("mian thread");
	return 0;
}

3.2使用线程的分离:

有一个问题:如果使用上述的阻塞线程pthread_join(tid,&retv);在多线程的情况下会影响效率。

所以 这里引出线程分离

两种方式:

1 使用pthread_detach

指定该状态,线程主动与主控线程断开关系。线程结束后(不会产生僵尸线程)

主进程和子线程里执行都可以

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

int* testThread(void *arg){
//pthread_detach(pthread_self());
printf("This is a thread test\n");
sleep(1);
pthread_exit("thread return");
}

int main(int argc, const char *argv[])
{
	pthread_t tid[5];
	int ret;
	int i;
	for(i = 0; i < 5 ;i++){ 
	ret = pthread_create(&tid[i],NULL,(void*)testThread,NULL);
	pthread_detach(tid[i]);
	}
	sleep(30);
	return 0;
}

2 创建线程时候设置为分离属性

  pthread_attr_t attr;

  pthread_attr_init(&attr);

  pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);

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

int* testThread(void *arg){
printf("This is a thread test\n");
sleep(1);
pthread_exit("thread return");
}

int main(int argc, const char *argv[])
{
	pthread_t tid[5];
	int ret;
	int i;
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	for(i = 0; i < 5 ;i++){ 
	ret = pthread_create(&tid[i],&attr,(void*)testThread,NULL);
	}
	sleep(30);
	return 0;
}

注意: 通过top -p [pid]来查看指定进程的动态信息

作业

练习创建多线程,并且实现线程的回收

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

void* threadtest(void*arg){

printf("线程 %d\n",(int)arg);
sleep(1);
pthread_exit("hha...");
}

int main(int argc, const char *argv[])
{
	int ret;
	pthread_t ptd[5];

	int i;
	for(i = 0;i < 5;i++){
	
	ret = pthread_create (&ptd[i],NULL,threadtest,(void*)i);
	pthread_detach(ptd[i]);
	
	}
	sleep(100);
	return 0;
}


总结

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值