Linux 多线程

目录

前言

多线程

        相关知识点

    多线程和多进程的区别

    LWP

    进程ID

    用户级线程ID

    内核级线程ID

线程控制

    线程的创建

    线程终止

    线程等待

    线程分离


前言

   线程是进程中的一个执行流程,线程是cpu调度的基本单元,而进程是系统进行资源分配的基本单元。

多线程

    概念:

    线程是进程中的一个执行流程,线程是cpu调度的基本单元(调度一段代码的执行是通过线程完成的)。linux下一个进程中是可以存在多个pcb的,一个pcb就是一个执行流程。

    提问:我们说进程就是pcb,是程序的运行过程描述,操作系统通过pcb管理程序的运行和调度,那么一个进程中有多个pcb和多个进程中有多个pcb在多执行流程使用中有什么区别?

    我们可以把进程和线程想象成工厂和工厂里的生产线一样的关系。多线程就是在一个工厂里多开几条生产线,多进程就是多开几个工厂。

    多线程和多进程各有好处:

    多线程:资源消耗小,但稳健性不如多进程。

    多进程:资源消耗大,但稳健性比多线程好。

结论:线程是cpu执行调度的基本单元,而linux下pcb是程序运行过程的描述,因此linux下线程是通过pcb来实现的。

    在其他操作系统中,比如Windows里,进程和线程不一样,他们各有各的描述。而linux下没有真正的线程,因为linux下的线程就是一个pcb,linux下的线程也被称为轻量级进程(LWP)

        相关知识点

        ·多线程中栈与堆的基本情况是:多个线程各自有一个栈,共有一个堆。

        ·线程没有自己的独立空间,线程只是在进程虚拟地址空间中拥有相对独立的一块空间,但是本质上说用的是同一个地址空间。

        一个线程不能独立执行程序:线程包含cpu现场,但是线程只是进程中的一个执行流,执行的是程序中的一个片段代码,多个线程完整整体程序的运行。

        线程和进程都可并发执行,线程的粒度小于进程,占用资源更少,因此通常多线程比多进程并发性更高。

        一个程序至少有一个进程的说法是错的。程序是静态的,不涉及进程,进程是程序运行时的实体,是一次程序的运行。

        线程使用公共变量/内存时需要使用同步机制,因为他们在同一地址空间内。

        可以使用ps -L命令查看轻量级进程信息。

        pthread_create函数是一个库函数, 代码当中如果使用该函数创建线程, 则需要在编译的时候链接“libpthread.so”线程库。(比如gcc -lpthread pthread2.c -o pthread2)

       

    多线程和多进程的区别

        进程是系统进行资源分配的基本单元(每运行一个程序,操作系统就要分配一次程序运行所需的资源)

        线程是cpu进行执行调度的基本单元,在linux下线程通过pcb来实现,一个进程中可以有多个pcb,因此线程也被称为轻量级进程LWP。

        ··多进程就是把多个任务分成多个程序,每个进程执行其中一个,多进程占用资源多,但是稳健性很好。

 

        ··多线程是将一个整体的程序分成三个模块,一个pcb调度其中一个模块,多线程占用资源少,但稳健性不如多进程。

         多线程的优点:

             ·1.线程间通信更加灵活(共享了虚拟地址)

             ·2.创建和销毁成本更低(线程之间很多资源是共享的,所以创建时不需要分配太多资源)

             ·3.同一个进程间中的线程间调度成本更低(cpu上加载的块表信息,页表指针等都不需要替换)。

        适用场景:

                  多进程:对程序的安全性要求大于资源和性能要求时。

                  多线程:对程序的安全性要求小于资源和性能要求以及其他情况时。

    LWP

        Linux下并没有真正意义上的线程,甚至可以说没有进程这个概念,Linux下只有task,其对应的数据结构为task_struct,这里为了方便说明以及按照比较主流的说法,我们将task称之为进程。

        所以LWP就是linux下的进程,也叫轻量级进程。

    进程ID

        这里所说的进程ID指我们通过fork创建子进程,子进程和父进程在内核中独立运行,并且一个进程对应一个进程描述符(PCB),PCB中包含了进程的ID,通过getpid返回当前进程ID

    用户级线程ID

        用户级线程,对线程的操控是由用户自己来完成,那么对此线程操控,用户知道你是哪一个线程,故此又有了用户态的线程ID;这里我们通过pthread_self()函数获得。

        用户级ID是个地址而不是整型数。

    内核级线程ID

        linux下,内核中,并不存在线程这一说,而是通过复制了进程的PCB作为标识自己(线程),作为进程的一个执行分支;既然有进程描述符(PCB)标识,自然就有一个标识符(ID)来标识着我是你(进程)的哪一个分支,这个标识符(ID)就是内核中的线程ID,通过syscall获得

线程控制

        POSIX线程库:与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的要使用这些函数库,要通过引入头文<pthread.h>链接这些线程函数库时要使用编译器命令的“-lpthread”选项

    线程的创建

        int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

    thread:返回线程ID;

    attr:设置线程属性,为NULL时是默认属性。

    start_routine:一个函数地址,线程启动后要执行的函数。

    arg:传给线程启动后函数的参数。

    创建一个线程:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>


void* Routine(void* arg){  //新线程
  char* msg=(char*)arg;
  while(1){
    printf("I am %s\n",msg);
    sleep(1);
  }
}

int main(){
  pthread_t tid;
  pthread_create(&tid,NULL,Routine,(void*)"thread 1");//创建一个新线程去执行Routine    
  while(1){      //主线程
    printf("I am main!\n");
    sleep(2);
  }

  return 0;
}

     创建多线程:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

void* Routine(void* arg){
  char* msg = (char*)arg;
  while(1){
    printf("I am %s    pid:%d    ppid:%d\n",msg,getpid(),getppid());
    sleep(1);
  }
}

int main(){
  pthread_t tid[5];
  for(int i=0;i<5;++i){
    char* buffer = (char*)malloc(64);
    sprintf(buffer,"thread %d",i);
    pthread_create(&tid[i],NULL,Routine,buffer);
  }
  while(1){
    printf("I am main thread    pid:%d    ppid:%d\n",getpid(),getppid());
    sleep(2);
  }

  return 0;                                                              
}

 可以看到创建的5个多线程的pid和ppid都是一样的。

    线程终止

        ①用return返回:

            在线程中使用return表示当前线程退出,如果是在main函数中使用return,则整个进程退出,也就是说当主线程退出后其他线程一起退出。

        ②pthread_exit函数

            void pthread_exit(void *retval);

                retval:线程退出时的退出码信息。

            任何一个线程调用exit都是吧表示整个进程退出。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

void* Routine(void* arg){
  char* msg = (char*)arg;
  int count=5;

  while(count--){
    printf("I am %s    pid:%d    ppid:%d    tid:%lu\n",msg,getpid(),getppid(),pthread_self());
    sleep(1);
  }

  pthread_exit((void*)101);
}

int main(){
  pthread_t tid[5];
  for(int i=0;i<5;++i){
    char* buffer = (char*)malloc(64);
    sprintf(buffer,"thread %d",i);
    pthread_create(&tid[i],NULL,Routine,buffer);
    printf("%s tid is %lu\n",buffer,tid[i]);
  }

    printf("I am main thread    pid:%d    ppid:%d    tid:%lu\n",getpid(),getppid(),pthread_self());

    for(int i=0;i<5;++i){
      void* ret=NULL;
      pthread_join(tid[i],&ret);                                                                    
      printf("thread %d[%lu] was quit\n",i,tid[i]);
    }


  return 0;
}

 

③pthread_cancel函数

    int pthread_cancel(pthread_t thread);

    thread:被取消的线程ID;

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

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

void* Routine(void* arg){
  char* msg = (char*)arg;
  int count=0;
  while(count<5){
    printf("I am %s    pid:%d    ppid:%d    tid:%lu\n",msg,getpid(),getppid(),pthread_self());
    sleep(1);
    count++;
    pthread_cancel(pthread_self());
  }
  pthread_exit((void*)101);
}

int main(){
  pthread_t tid[5];
  for(int i=0;i<5;++i){
    char* buffer = (char*)malloc(64);
    sprintf(buffer,"thread %d",i);
    pthread_create(&tid[i],NULL,Routine,buffer);
    printf("%s tid is %lu\n",buffer,tid[i]);
  }
  
    printf("I am main thread    pid:%d    ppid:%d    tid:%lu\n",getpid(),getppid(),pthread_self());

    for(int i=0;i<5;++i){
      void* ret=NULL;
      pthread_join(tid[i],&ret);
      printf("thread %d[%lu] was quit\n",i,tid[i]);
    }                                                                                              
  return 0;
}          

 

    线程等待

        已经退出的线程,其空间并没有被释放。而创建新线程也不会用刚才退出的线程的空间,线程和进程一样也需要被等待,如果没有线程等待,那么这个线程的资源不会被回收。所以我们需要线程等待来避免内存泄漏。

        函数:int pthread_join(pthread_t thread, void **retval);

        thread:被等待的线程ID;

        retval:线程退出时的退出码信息。

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

    调用该函数的线程将会阻塞挂起等待,直到ID为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的。

   

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

void* Routine(void* arg){
  char* msg = (char*)arg;
  int count=5;
  while(count--){
    printf("I am %s    pid:%d    ppid:%d    tid:%lu\n",msg,getpid(),getppid(),pthread_self());
    sleep(1);
  }
  return ((void*)101);
}

int main(){
  pthread_t tid[5];
  for(int i=0;i<5;++i){
    char* buffer = (char*)malloc(64);
    sprintf(buffer,"thread %d",i);
    pthread_create(&tid[i],NULL,Routine,buffer);
    printf("%s tid is %lu\n",buffer,tid[i]);
  }

    printf("I am main thread    pid:%d    ppid:%d    tid:%lu\n",getpid(),getppid(),pthread_self());

    for(int i=0;i<5;++i){
      void* ret=NULL;
      pthread_join(tid[i],&ret);
      printf("thread %d[%lu] was quit,exitcode:%d\n",i,tid[i],(int*)ret);                          
    }
  return 0;
}

 ··线程退出拿到的退出码只能判断结果是否正确 

如果某个线程崩了,整个进程也会崩溃,这也说明了多线程的不稳健性。

    线程分离

        在默认情况下,新创建的线程都是joinable的,需要主线程对它进行等待来回收资源。但如果我们不关心线程的返回值,那么join就是个负担,所以我们可以将该线程分离,分离后线程退出时会自动释放资源。

        一个线程如果被分离,它依然会使用该进程的资源,依然在该进程里运行,线程崩了也会影响其他线程。只不过该线程退出时不需要主线程去等待了。

        joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

    函数:  int pthread_detach(pthread_t thread)

    thread:被分离线程的ID。

    分离成功返回0,失败返回错误码。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

void* Routine(void* arg){
  pthread_detach(pthread_self());//线程的分离
  char* msg = (char*)arg;
  int count=0;
  while(count<3){
    printf("I am %s    pid:%d    ppid:%d    tid:%lu\n",msg,getpid(),getppid(),pthread_self());
    sleep(1);
    count++;
  }
  pthread_exit((void*)101);
}

int main(){
  pthread_t tid[5];
  for(int i=0;i<5;++i){
    char* buffer = (char*)malloc(64);
    sprintf(buffer,"thread %d",i);
    pthread_create(&tid[i],NULL,Routine,buffer);
    printf("%s tid is %lu\n",buffer,tid[i]);
  }
                                                                                                   
  while(1){
    printf("I am main thread    pid:%d    ppid:%d    tid:%lu\n",getpid(),getppid(),pthread_self());
    sleep(1);
  }

  return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值