当前位置: 首页 > article >正文

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

      • 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;
}


http://www.kler.cn/news/308600.html

相关文章:

  • Leetcode—1184. 公交站间的距离【简单】
  • Linux(CentOS8)服务器安装RabbitMQ
  • Python数据分析工具(一):Requests的用法
  • 游戏中的唯一id生成,防止合服id重复
  • 成功激活mac idea 记录
  • Java封装(面向对象)
  • 104.WEB渗透测试-信息收集-FOFA语法(4)
  • 腾讯百度阿里华为常见算法面试题TOP100(4):双指针、哈希、滑动窗口
  • [go] 命令模式
  • 电信创维光猫DT741超级密码
  • 【LeetCode】每日一题 2024_9_13 预算内的最多机器人数目(滑动窗口、单调队列)
  • 文件标识符fd
  • 嵌入式Linux学习笔记(5)-进程间常见通讯方式(c语言实现)
  • 09_Python流程控制_分支
  • win10怎么配置dnat规则,访问win10的网口A ip的6443端口,映射到1.1.1.1的6443端口去
  • Android 源码集成可卸载 APP
  • go多线程
  • python-在PyCharm中使用PyQt5
  • 【C++】多态详解
  • mysql学习教程,从入门到精通,SQL IN BETWEEN 运算符(13)
  • 基于STM32F407ZGT6——看门狗
  • new/delete和malloc/free到底有什么区别
  • docker镜像结构
  • 代码随想录:动态规划4-5
  • Java技术深度探索:高并发场景下的线程安全与性能优化
  • java面试题-Sql 语句的执行顺序
  • 【SOP】使用MMDeploy将MMAction2的模型转换为TensorRT
  • 二叉树的前中后序遍历(递归法)( 含leetcode上三道【前中后序】遍历题目)
  • java-lambda-常用方法总结汇总
  • 【乐企】旅客运输发票接口实现