Linux-线程概念-线程控制-进程和线程对比-线程创建

Posted 天津 唐秙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux-线程概念-线程控制-进程和线程对比-线程创建相关的知识,希望对你有一定的参考价值。

1. 线程概念

  1.一个进程当中一定存在一个主线程,执行main函数的线程就称为主线程。
  2.其他线程都被称为工作线程。
在这里插入图片描述
  3.之前我们说到的进程,本质上是线程组,换句话说,线程组被称为进程。
  4.线程也可以称之为轻量级进程(LWP),操作系统内核当中,压根是没有线程概念的。
  5.

pid_t pid;
pid_t tgid;

  pid:轻量级进程id,也被称之为线程id
  tgid:轻量级进程组id,也被称之为进程id
  6.在一个进程当中,不管这个进程由多少个线程,在所有的线程的PCB当中,tgid都是相等的。
  7.主线程(执行main函数的LWP)的pid和tgid相等。
  8.处理主线程,工作线程的pid,都是不一样的,可以用pid去区分到底是哪一个线程。
在这里插入图片描述
线程的共享与独有:
  独有: 在共享区当中由自己的调用堆栈,寄存器,线程ID,errno,信号屏蔽字,调度优先级(PR)。
  共享: 文件描述符(fd_array[xxx]),当前进程工作目录,用户id和用户id,信号的处理方式。

并发:多个执行流在同一时刻只能有一个执行流拥有cpu进行运算。
线程的优点:
  1.一个进程当中多个执行流可以并行的执行代码,就可以提高程序的运行效率。
  2.进程切换要比线程切换操作系统付出的代价大。
  3.线程占用的资源要比进程少很多
  4.可并行的运行
线程的缺点:
  1.当一个进程当中,线程数量远远大于cpu数量的时候,就有可能线程切换的开销影响线程的效率,因此,程序当中的线程数量不是越多越好,这是有一个阈值的,想要得到阈值就要进行压力测试,这块可能就会牵扯到高并发。
  2.健壮性降低,健壮性和鲁棒性都是描述代码运行稳定的词语。
  3.缺乏访问控制
  4.编写难度高,主线程和工作线程的时序性不好控制。

  总结:线程(LWP)是操作系统调度的基本单位,进程是操作系统资源分配的基本单位。

2. 线程控制

2.1 进程和线程的对比

  1.进程的健壮性要比线程好,多进程的应用场景:shell,守护进程,分布式服务,守护进程和被守护的进程之间用的是共享内存,共享内存当中给守护进程传递时间,如果几次传递的时间都是没有发生改变的,那么就说明子进程挂掉了,为了24小时不间断服务,守护进程会杀死子进程,然后重新拉起来一个子进程,但是就算是这样,也并不能保证24小时不间断服务,因此还存在一个分布式服务,会存在多个服务端。
  这里就涉及到了nginx中的负载均衡和路由转发,负载均衡是说会根据服务器性能的好坏,对不同的服务器按一定的比例转发,路由转发是负则用户将消息给对应的服务器发送,如果某一个服务器挂掉了,就默认不会给那个服务器发送。
  2.多线程要比多进程耗费资源小,并且切换快,程序运行效率高。

2.2 线程创建

接口:
在这里插入图片描述
int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void * (* start_routine) (void * ), void * arg);
   pthread_t:线程的标识符,本质上是一个线程在共享区独有空间的首地址
   thread:是一个出参,该值是由pthread_create函数赋值的
  pthread_attr_t:创建线程的属性,一般情况下指定为NULL,采用默认属性
  void * (* start_routine)(void)
    函数指针:接受一个返回值为void* ,参数为void* 的函数地址,就是线程的入口函数(相当于程序的主函数)
  void* arg:给线程入口函数传递的参数
    由于参数的类型是void*,所以给了程序无限传递参数的方式。
    char*,int*,结构体指针,this指针
  返回值:
    失败:<0
    成功:=0

  多线程程序的makefile文件和普通的程序编译文件是不一样的,如果用普通的编译的方法,会出现下面这种情况。
在这里插入图片描述

  在编译多线程程序的时候,一定要链接libpthread.so文件 -lpthread
命令: g++ thread_create.cpp -o thread_create -lpthread
1.主线程和工作线程都是死循环

#include <stdio.h>    
#include <unistd.h>    
#include <pthread.h>    
                                                                         
void* MyThreadStart(void* arg)    
  {    
      while(1)    
      {    
          printf("i am MyThreadStart\\n");    
          sleep(1);    
      }    
  }    
  
  int main()    
  {    
      pthread_t tid;    
  
      int ret = pthread_create(&tid, NULL, MyThreadStart, NULL);    
      if(ret < 0)    
      {    
          perror("pthread_create fail");    
          return 0;    
      }    
  
      while(1)    
      {    
          printf("i am main\\n");    
          sleep(1);    
      }
    
      return 0;
  }

在这里插入图片描述
  通过调用堆栈验证,可以查看主线程和工作线程,有调用main的是主线程,另一个是工作线程,或者根据线程号,进程号和线程号相同的为主线程,其余均为工作线程。

在这里插入图片描述
   之后尝试把主线程中的循环去掉,也就是说主线程退出,工作线程不退出,查看进程的情况,结果是进程结束,也就是说主线程退出后,工作线程也会退出。

命令: top -H -p 20022(pid)
功能: 可以查看进程中的线程执行情况
在这里插入图片描述
2.工作进程死循环,将主线程中的循环去掉

#include <stdio.h>    
#include <unistd.h>    
#include <pthread.h>    
  
void* MyThreadStart(void* arg)    
{    
	while(1)    
    {    
		printf("i am MyThreadStart\\n");    
        sleep(1);    
    }    
}    
  
int main()    
{    
    pthread_t tid;    
  
    int ret = pthread_create(&tid, NULL, MyThreadStart, NULL);    
    if(ret < 0)    
    {    
        perror("pthread_create fail");    
        return 0;    
    }    
    return 0;                                                              
}    

在这里插入图片描述

3.传递一个i=1,2,3,4

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

struct ThreadId    
{    
    int thread_id_;    
};    

void* MyThreadStart(void* arg)    
{    
    struct ThreadId* ti = (struct ThreadId*)arg;    
    while(1)    
    {    
        printf("i am MyThreadStart, i = %d\\n", ti->thread_id_);    
        sleep(1);    
    }    
    delete ti;    
}    
int main()
{
    pthread_t tid;

    for(int i = 0; i < 4; i++)
    {
        struct ThreadId* ti = new ThreadId();
        ti->thread_id_ = i;
    
        int ret = pthread_create(&tid, NULL, MyThreadStart, (void*)ti);
        if(ret < 0)
        {
           perror("pthread_create fail");
           return 0;
        }
    }

    //create success
    while(1)
    {
   	    printf("i am main thread\\n");
        sleep(1);
    }

    return 0;
}   

在这里插入图片描述
  正常来说for循环是按照1234的顺序传递的,打印出来应该也是1234,但是,由于线程之间是抢占式的执行方式,所以输出的顺序可能不同。

以上是关于Linux-线程概念-线程控制-进程和线程对比-线程创建的主要内容,如果未能解决你的问题,请参考以下文章

Linux:详解多线程(线程概念线程控制—线程创建线程终止线程等待)

Linux系统编程 多线程

Linux系统编程 多线程

Linux从青铜到王者第十三篇:Linux多线程四万字详解

Unix线程概念控制原语属性

C++笔记--Linux编程(13)-守护进程-线程