Linux --- 线程 之 线程与多线程 (进程与线程,多进程与多线程的区别)


前言


提示:以下是本篇文章正文内容,下面案例可供参考

1、线程概念及与进程区别

1.1、线程基本概念

  • 线程是进程中的一条执行流程是cpu调度运行的基本单元
  • Linux 下,通过pcb实现 ,一个进程中可有多个pcb,其共享进程中的大部分资源,相较于传统pcb较为轻量化,故也被称为轻量级进程

1.2、线程和进程

其实Linux下,线程和进程的概念较为模糊,故换一个角度

  • 进程cpu资源管理的基本单元
  • 线程是cpu调度执行的基本单元

1.2.1、线程之间独有与共享

线程之间独有与共享:

  • 独有
    1.标识符 ——(操作系统通过不同的标识符,区分不同的pcb
    2.上下文数据,程序计数器 ——(使程序可独立调度,保有寄存器中的数据)
    3.栈 ——(共用同一虚拟地址空间,独立的栈可防止调用栈混乱
    4.信号屏蔽字 ——(每个线程可独立屏蔽自己的信号,防止事件的被打断)
    5.errno —— (当两个线程调用同一个系统调用接口,可能存在前一个errno值被后一个覆盖故相对独立)

  • 共享
    1.虚拟地址空间 ,页表 ——(映射同一块物理空间,较进程间通信,更为高效,可通过全局变量,传参实现通信)
    2.IO信息——(一个线程接受的文件描述符给交给另外一个线程,对其进行处理
    3.信号处理方式——(信号的处理是针对整个进程,而进程又包含多个线程,若不进行统一,线程处理可能会产生歧义

1.2.2、多进程与多线程的区别

  • 多线程

        1.线程间通信更加灵活
        2.同一进程的线程间调度切换成本要更低一些
        3.线程的创建与销毁成本(时间成本和资源占用率成本)更低
    
  • 多进程

         1.稳定性,健壮性强
         2.有些系统调用接口是针对进程(信号,exit)
    
  • 两者共同的优点,即并发处理的优点,大大提升了多任务处理的效率;

    • 场景介绍:
      1.CPU密集型程序:即程序中几乎都是大量的数据运算处理,此时采用多执行流处理,可以充分利用cpu资源,从而大大提高效率;(但是对于单核cpu,同一时间只允许一个线程,考虑线程之间的切换,还不如单个执行流~~);
      2.IO密集型程序: 即程序中几乎都是大量IO的操作,但是IO操作实则就是等待IO操作相关描述符就绪,之后在进行数据拷贝的过程。(cpu计算速度和IO读取数据速度相差太多,进而导致效率较低)因而采用多执行流,可以间接性压缩IO等待时长,提高程序效率!(但是执行流不是越多越好,因为大量执行流就涉及到之间切换调度,可能存在切换调度成本过高现象,需要进行压力测试,从而得到效率最高的执行流数目~~);

2、线程控制

2.1、线程创建

2.1.1、基本介绍

Linux下操作系统下并没有提供用于实现线程的系统调用接口,故我们所使用的大佬们对一些特殊系统调用接口在上层用户态进行封装的一套操作线程的库函数接口

2.1.2、线程创建接口介绍

线程创建:
  int pthread_create(pthread_t* tid,
  					const pthread_attr_t* attr,
  					void*(*start_routine) (void *),
  					void *arg);
 参数:  tid    ----  获取线程的id(实则为线程描述空间首地址)
 		attr  ----	创建线程属性,常置NULL
start_routine ----	线程入口函数地址(参数和返回值都为 void*)
        arg   ----  线程入口函数的参数,不关心可置为NULL
返回值: 成功返回 0,错误返回 非零
  • 那么改如何去理解tid,这个参数呢???
    在这里插入图片描述

2.1.3、接口使用

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* entry(void* arge)
{
  while(1)
  {
    printf("i am child pthread!\n");
    sleep(3);
  }
  return NULL;
}
int main()
{
  int ret;
  pthread_t tid;
  ret=pthread_create(&tid,NULL,entry,NULL);
  if(ret!=0)
  {
    printf("pthread create error!\n");
  }
  while(1)
  {
    printf("i am main pthread!\n");
    sleep(3);
  }
  return 0;
}
  • 打印结果
    在这里插入图片描述

2.2、终止线程

2.2.1、线程终止方式

  • 1. 在线程入口函数中return—线程入口函数运行完毕,线程退出
 #include <stdio.h>
 #include <pthread.h>
 #include <unistd.h>
 void* entry(void* arge)
 {
   sleep(3);
   return NULL;
 }
 int main()
 {
     pthread_t* tid;
     //创建线程
     int ret=pthread_create(tid,NULL,entry,NULL);
     if(ret!=0)
     {
       perror("creat error\n");                                   
       return -1;
     }
     return 0;
 }

在这里插入图片描述

  • 2.函数接口,进行终止进程
  pthread_t exit(void* value_ptr);
参数: value_ptr 为线程的返回值(不要指向一个局部变量)
  int cancel(pthread_t thread);
参数: thread 为指定终止线程的id
返回值:成功返回0,错误返回错误码(非零):一个线程时被取消的,则这个线程的返回值为:PTHREAD_CANCEL--- '-1'

两个函数可在任意位置进行调用进行的线程的终止

2.4、线程等待

2.4.1、线程等待概念

  • 由于一个线程的退出,其退出后默认不会自动释放所有资源(其pcb会被释放,但线程描述信息保存在虚拟地址中一个相对的独立的空间中并没有被释放)
  • 需要其他线程进行等待,获取其返回值,然后释放所有资源

2.4.2、线程等待接口

相关接口
int pthread_join(pthread_t tid,void** retval)
参数: tid 指定线程的id
     retval:用于获取线程的返回值
返回值:成功返回0,失败返回错误编号 (非零)     

2.4.3、接口运用与分析

  • 1. 主函数,创建线程并且等待线程用retavl获取线程的返回值
int main()
{
  pthread_t tid;
  //创建线程
  int ret=pthread_create(&tid,NULL,entry,NULL);
  if(ret!=0)
  {
    perror("creat error\n");
    return -1;
  }
  void* retavl;
  pthread_join(tid,&retavl);
  printf("tip:%p----%s\n",tid,retavl);
  while(1)
  {
    printf("i am main thread\n");
    sleep(1);
  }
  return 0;

  • 2. 线程的入口函数,其中func()函数用终止线程
//线程入口函数
void* entry(void* arge)
{
  func();
  while(1)
  {
    printf("i am child thread!\n");
    sleep(1);
  }
  sleep(3);
  return NULL;
}
  • 3. func()函数用于终止线程,并设置其返回值为苦尼叽哇
void func()                                                                        
{
	//终止线程 并设置返回值为“苦尼叽哇~~”
  char* retval="苦尼叽哇~~\n";
  pthread_exit(retval);
}
  • 4. 打印结果
    由于线程入口函数中的func()函数对线程的终止,故无后续内容打印;
    再者main函数,pthread_join对线程返回值的接受,并存于retavl中;
    最后main函数,继续打印 “i am main thread” ;
    在这里插入图片描述

2.5、线程分离

2.5.1、线程分离状态特性

  • 线程退出,我们需通过pthread_join获取其线程的退出返回值,进而释放其资源,防止资源泄漏,进而进行程序的后续处理!!
  • 那么当我们对线程的要求,仅仅是让其完成相应的功能,而不关心其退出的返回值,那我们是不是还不得不进行线程的等待了吗???
    答案当然,不是!!!

其实每个线程中都有一个属性–分离属性

  • 默认是joinable状态,处于joinable状态的线程退出后不会自动释放资源需要等待被等待
  • 分离属性,还有一个状态,detach状态,处于detach状态的线程退出后,反之,则会立即释放资源,无需等待(此状态则满足的上述的需求)

2.5.2、线程分离接口

  线程分离(可在程序任意位置调用)
  int pthread_detach(pthread_t tid);
  参数: tid 指定要分离的线程
  返回值:成功返回0.错误,返回错误码--(非零)
  ERRORS
       EINVAL thread is not a joinable thread.
						(线程不在joinable状态,即已被分离)
       ESRCH  No thread with the ID thread could be found.
						(找不到指定的线程)
  • 在上述,线程等待的代码增添线程分离接口
int main()
{
  pthread_t tid;
  //创建线程
  int ret=pthread_create(&tid,NULL,entry,NULL);
  if(ret!=0)
  {
    perror("creat error\n");
    return -1;
  }
  
  //增添线程分离
  pthread_detach(tid);
  
  
  void* retavl;
  pthread_join(tid,&retavl);
  printf("tip:%p----%s\n",tid,retavl);
  while(1)
  {
    printf("i am main thread\n");
    sleep(1);
  }
  return 0;

  • 由于线程在等待接受返回值之前,进行了线程分离,释放了资源
  • 后续打印接收返回值的retavl则是一堆乱码
    在这里插入图片描述

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值