Linux学习记录十四----------线程的创建和回收
文章目录
- 五、Linux线程
- 1.守护进程
- 1.1.守护进程的特点
- 1.2.进程组
- 1.3会话
- 1.4创建守护进程模型
- 2.线程的概念
- 3.线程的创建及相关函数
- 3.1.创建线程‐‐pthread_create
- 3.2.单个线程退出 --pthread_exit
- 3.3.阻塞等待线程退出,获取线程退出状态--pthread_join
- 3.4.线程分离函数--pthread_detach
- 3.5.杀死(取消)线程--pthread_cancel
- 3.6.比较两个线程ID是否相等(预留函数) --pthread_equal
- 4.线程的分离属性
五、Linux线程
1.守护进程
守护进程是一种长期运行的进程(守护进程的生存期不一定长,但一般应该这样做),一般是操作系统启动的时候它就启动,操作系统关闭的时候它才关闭。守护进程跟终端无关联,也就是说它们没有控制终端,所以控制终端退出,也不会导致守护进程退出。守护进程是在后台运行的,不会占着终端,终端可以执行其他命令。Linux 操作系统本身是有很多的守护进程在默默地运行,维持着系统的日常活动。
1.1.守护进程的特点
-
后台服务进程
-
独立于控制终端
-
周期性执行某任务
-
不受用户登录注销影响
-
一般采用以d结尾的名字(服务)
1.2.进程组
- 进程的组长
- 组里边的第一进程
- 进程组的ID==进程中的组长的ID
- 进程中组长的选择
- 进程中的第一个进程
- 进程组ID的设定
- 进程组的ID就是组长的进程ID
1.3会话
-
创建一个会话注意事项
- 不能是进程组长
- 创建会话的进程成为新进程组的组长
- 有些lInux版本需要root权限执行此操作
- 创建出的新会话会丢弃原有的控制终端 一般步骤;fork ,父亲死,儿子执行创建会话操作(setsid)
-
获取进程所属的会话ID
- pid_t getsid(pid_t pid);
-
创建一个会话
- pid_t setsid(void);
1.4创建守护进程模型
-
fork子进程,父进程退出 ----必须
-
子进程创建新会话 -------必须
-
改变当前工作目录chdir —非必须
-
重设文件掩码 --非必须
-
关闭文件描述符 --非必须
-
执行核心工作----必须
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
pid = fork();
if(pid == 0)
{
pid_t sid;
sid = setsid();//创建会话
while(1){//执行核心工作
sleep(1);
}
}
else if(pid > 0)
{
exit(0);//父进程退出
}
return 0;
}
2.线程的概念
在Linux下: 线程就是进程-轻量级进程 对于内核来货,线程就是进程 多进程和多线程的区别:
多进程: 始终共享的资源 代码、文件描述符、内存映射区–mmap
多线程:始终共享的资源:堆、全局变量,节省资源
-
主线程和子线程
- 共享: .text .bss .data 堆 动态加载区 环境变量 命令行参数
-
通信:全局变量,堆
-
不共享栈
- eg一共五个线程,栈区被平均分成五块
-
查看指定线程的LWP号:
- 线程号和线程ID是有区别的
- 线程号是给内核看的
- 线程号和线程ID是有区别的
-
查看方式
-
找到程序的进程ID
- ps -Lf pid
- top -p tid(线程状态动态显示)
-
3.线程的创建及相关函数
3.1.创建线程‐‐pthread_create
int pthread_create( pthread_t *thread), //线程ID = 无符号长整型
const pthread_attr_t *attr, //线程属性,NULL
void *(*start_routine)(void *), //线程处理函数
void *arg); //线程处理函数
/*@param:
* pthread:传出参数,线程创建成功之后,会被设置一个合适的值
* attr:默认传NULL
* start_routine:子线程的处理函数
* arg: 回调函数的参数
*@return:成功:0,错误:错误号 //perror不能使用该函数打印错误信息
*/
注意:
主线程先退出,子线程会被强制结束
验证线程直接共享全局变量
创建线程
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
int i = 0;
void * pthread_son(void *arg)
{
printf("create pthread successed pthid is %ld\n",pthread_self());
while(i++)
{
printf("i = %d\n",i);
sleep(1);
}
}
int main()
{
pthread_t pthid;
int ret;
ret = pthread_create(&pthid,NULL,pthread_son,NULL);
if(ret != 0)
{
printf("error num is %d\n",ret);
printf("%s\n",strerror(ret));
return -1;
}
printf("create pthread successed parent pthid is %ld\n",pthread_self());
for(;i < 5;i++)
{
printf("i = %d\n",i);
}
sleep(3);
return 0;
}
3.2.单个线程退出 --pthread_exit
不论是主线程还是子线程调用exit(0)后整个所以线程都会结束,而调用pthread_exit()只会退出当前进程
函数原型: void pthread‐exit(void *retval);
retval指针:必须指向全局,堆
3.3.阻塞等待线程退出,获取线程退出状态–pthread_join
函数原型:
int pthread_join(pthread_t pthread, void **retval)
/*参数:
* pthread:要回收的子线程的ID
* retval:读取线程退出的携带信息
* 传出参数
* void* ptr;
*/
pthread_join(pthid,&ptr);
指向的内存和pthread_exit参数指向地址一致
单个线程退出并回收
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
int i = 0;
int num = 100;
void * pthread_son(void *arg)
{
printf("create pthread successed pthid is %ld\n",pthread_self());
while(i++)
{
printf("i = %d\n",i);
sleep(1);
if(i == 10)
{
printf("exit pthread_son\n");
pthread_exit(&num);//退出子线程
}
}
}
int main()
{
pthread_t pthid;
int ret;
ret = pthread_create(&pthid,NULL,pthread_son,NULL);
if(ret != 0)
{
printf("error num is %d\n",ret);
printf("%s\n",strerror(ret));
return -1;
}
printf("create pthread successed parent pthid is %ld\n",pthread_self());
for(;i < 5;i++)
{
printf("i = %d\n",i);
}
void *ptr;
pthread_join(pthid,&ptr);//回收线程ID为pthid的子线程
printf("join child pthread num is %d\n",*(int *)ptr);
return 0;
}
3.4.线程分离函数–pthread_detach
函数原型:int pthread_datach(pthread_t thread);
调用该函数之后不需要 pthread_join
子线程会自动回收自己的PCB
3.5.杀死(取消)线程–pthread_cancel
函数原型: int pthread_cancel(pthread_t pthread);
pthread_testcancel();设置取消点—在取消点时就会杀死该线程
注:
-
使用注意事项:
- 在要杀死的子线程对应的处理的函数的内部,必须做过一次系统调用 (阻塞调用)
- write read printf
- int a = 2; int b = a+3;
- 否则需要手动设置取消点
int pthread_setcanceltype(int type,int *oldtype) PTHREAD_CANCEL_DEFERRED//等待目标点才会取消 PTHREAD——CANCEL_ASYNCHRONOUS//目标线程会立即取消
3.6.比较两个线程ID是否相等(预留函数) --pthread_equal
函数原型:
int pthread_equal(pthread_t t1,pthread_t t2);
4.线程的分离属性
如果不回收线程资源会造成内存泄漏,且join不能完全回收线程资源
通过属性设置线程的分离
1.线程属性类型: pthread_attr_t attr;
2.线程属性操作函数:
- 对线程属性变量的初始化
- int pthread_attr_init(pthread_attr_t* attr);
- 设置线程分离属性
- int pthread_attr_setdetachstate( pthread_attr_t* attr, int detachstate );
- 参数:
- attr : 线程属性
- detachstate PTHREAD_CREATE_DETACHED(分离) PTHREAD_CREATE_JOINABLE(非分离)
- 释放线程资源函数 int pthread_attr_destroy(pthread_attr_t* attr);
设置分离属性
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
int i = 0;
int num = 100;
void * pthread_son(void *arg)
{
printf("create pthread successed pthid is %ld\n",pthread_self());
while(i++)
{
printf("i = %d\n",i);
sleep(1);
if(i == 10)
{
printf("exit pthread_son\n");
pthread_exit(&num);
}
}
}
int main()
{
pthread_t pthid;
int ret;
pthread_attr_t attr;
pthread_attr_init(&attr);//初始化线程属性变量
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置线程属性
ret = pthread_create(&pthid,&attr,pthread_son,NULL);
if(ret != 0)
{
printf("error num is %d\n",ret);
printf("%s\n",strerror(ret));
return -1;
}
printf("create pthread successed parent pthid is %ld\n",pthread_self());
for(;i < 5;i++)
{
printf("i = %d\n",i);
}
sleep(15);
pthread_attr_destroy(&attr);//释放线程资源函数
return 0;
}