加入收藏 | 设为首页 | 会员中心 | 我要投稿 我爱制作网_沈阳站长网 (https://www.024zz.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

Linux-线程学习(上)

发布时间:2022-11-17 11:19:32 所属栏目:Linux 来源:
导读:  本文导航:

  内容所占百分比

  线程概念

  40%

  线程与进程区别与联系

  20%

  线程优缺点

  10%

  线程控制(创建,终止,等待)

  30%

  线程
  本文导航:
 
  内容所占百分比
 
  线程概念
 
  40%
 
  线程与进程区别与联系
 
  20%
 
  线程优缺点
 
  10%
 
  线程控制(创建,终止,等待)
 
  30%
 
  线程的概念
 
  谈到线程,我们先从进程说起。
 
  我们写的程序从硬盘加载到内存开始运行时,进程就产生了。也就是操作系统开始为这个程序创建PCB,分配系统资源,比如分配一块虚拟地址空间,一个页表,一块物理内存。当这个进程内部有多个执行流时,现在我们可以简单理解父进程用vfork()创建了多个子进程时,这些子进程也需要资源。如果能分得资源就会独自分的资源,但如果分不了就共享。共享一块地址空间。共享一个页表和一块物理内存。
 
  为了便于来理解,我画了一张简明图
 
   有3个PCB,在以前的认知里,我们叫做3个进程。
 
  现在我们需要引入线程的概念。
 
  线程概念
 
  进程到线程
 
  这里写图片描述
 
  在明确线程概念后,我们就要站在线程的角度去理解上面那张图了。
 
  图中3个PCB ,我们就要把他们称之为3个线程了。因为这是在进程里的3个执行控制流。他们共享一块虚拟地址空间。一块页表,所以这3个线程所看到的物理类存也是一样的。但是他们可以执行各自的任务,并有自己生命特征。
 
  所以在图中,进程就是创建执行代码,创建PCB,申请资源,分配资源的实体,即整个图。而线程就是那些单个PCB,在线程中称之为TCB。
 
  或许下面这张图有助于来理解。
 
  这里写图片描述
 
  线程特点
 
  其中最重要的数据是栈和寄存器。私有栈是为了保存临时变量,便于函数调用等操作。私有寄存器是为了方便线程切换,保存上下文。
 
  这里写图片描述
 
  进程的多个线程共享
 
  .同地址空间,因此Test Segment,Data Segment都是共享的,如果定义个函数雇各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
 
  进程与线程联系
 
  在Linux下并没有专门为线程设计这么一个概念,也就是说没有真正意义上的线程,它在linux下是由进程模拟的,可以认为所有的PCB都可以称为轻量级进程(不一定是进程,也可能是线程)。进程是分配系统资源的一个实体,而线程是CPU调度的基本单位。
 
  在单个程序中同时运行多个线程完成不同的工作,称为多线程。相对来说,多进程相对稳定,多线程相对不稳定。可以认为,进程是分配系统资源的一个实体,而线程是CPU调度的基本单位。
 
  进程具有独立的地址空间,而线程并没有,同一进程内部的线程共享进程的地址空间。
 
  由于Linx下没有真正的线程,Linux用进程来描述线程和组织线程。所以在Linux下只有轻量级的线程,操作系统是看不到线程的TCB的。所以下图中的 第四个图:多个进程多个线程图中,操作系统可以看到的是6个PCB,
 
  线程的优点 线程的缺点 线程控制 POSIX线程库
 
  与线程有关的函数构成了一个完整的系列,绝?大多数函数的名字都是以“pthread_”打头的
 
  要使用这些函数库,要通过引入头文件, 链接这些线程函数库时要使用编译器命令的“-lpthread”选项
 
  创建线程
 
  调用函数
 
  返回值:成功返回0,失败返回 错误码
 
  错误检查:
 
  传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
 
  pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回 pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值业判定,因为读取返回值要比读取线程内的errno变量的开销更小
 
  测试用例
 
  注意:编译链接时,一定要加上-lpthreead选项,见图中命令部分。因为这是用户级别的库,并不是系统提供的库。
 
  #include
  #include
  #include
  //新线程的例程(执行任务,打印字符串)
  void * my_run(  void *arg)
  {
        while(1)
        {
              printf(  "  i am %s\n",(  char *)arg);
              sleep( 1);
        }
  }
  int main(  )
  {
        //申明一个本地变量tid,用来保存对等线程的ID
        pthread_t tid;
        //调用pthread_create函数创建一个新的线程,
        pthread_create(&tid,NULL,my_run,"newthread");
        //调用结束,同时运行,并且tid包含了新线程的id
        while(1)
        {
              printf(  "  i am main thread\n");
              sleep(2);
        }
        return 0;
  }
 
  pthread_create创建成功!
 
  主线程和新线程各自打印不同的内容。
 
  正常情况下,一个程序里是不会同时执行两个死循环的。但是,在这个程序里,居然可以同时执行两个死循环。原因就在于这时两个线程在运行。各自执行不同的任务,有自己的栈空间和寄存器,支持上下文切换和函数调用。
 
  终止线程
 
  如果需要只终止某个线程而不终止整个进程,可以有以下方法:
 
  void * my_run(  void *arg)
  {
        while(1)
        {
              printf(  "  i am %s\n",(  char *)arg);
              sleep( 1);
              return NULL;
        }
  }
  这里写图片描述
 
  新线程终止,主线程继续执行。
 
  - 线程可以调用pthread_ exit终止自己。
 
  pthread exit函数
 
  功能:线程终止
 
   #include
   void pthread_exit(void *retval);
  参数
 
  value ptr:value ptr不要指向一个局部变量。
 
  返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
 
  #include
  #include
  #include
  #include
  void * my_run(  void *arg)
  {
        printf("thread 1 returning ....\n");
        int *p=(int *)malloc(sizeof(int));
        *p=1;
        pthread_exit((void *)p);
  }
  int main(  )
  {
        pthread_t tid;
        void *ret;
        //thread 1 exit
        pthread_create(&tid,NULL,my_run,NULL);
        //等待新线程退出,退出码保存在ret中
        pthread_join(tid,&ret);
        printf("thread  return,id is:%ld,return code:%d\n",tid,*(int *)ret);
        free(ret);
        while(1)
        {
              printf("i am main thread\n");
              sleep(2);
        }
        return 0;
  }
  ,pthread exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
 
  调用phread_exit(),他会等待所有其他对等线程(就是同一个进程中的除自身外其他线程)终止,然后再终止主线程和整个进程。
 
  如果某个对等线程调用linux的exit函数,则该函数终止进程以及所有与该进程相关的线程
 
  pthread_cancel函数
 
  功能:取消一个执行中的线程
 
  #include
  int pthread_cancel(pthread_t thread);
  参数
 
  thread:线程ID
 
  返回值:成功返回0;失败返回错误码
 
  void* my_run_2(void* arg)
  {
        while(1)
        {
              printf(  "thread 2 is running...\n");
              sleep(1);
        }
        return NULL;
  }
  int main(  )
  {
        pthread_t tid;
        void *ret;
        pthread_create(&tid,NULL,my_run_2,NULL);
        sleep(1);
        //取消执行中的线程2
        pthread_cancel(tid);
        //等待线程2的返回状况,获取返回值
        pthread_join(tid,&ret);
        if(ret==PTHREAD_CANCELED)
        {
          printf("thread  return,id is:%ld,return code:PTHREAD_CANCELED\n",tid);
        }
        else
        {
          printf("thread  return,id is:%ld,return code:NULL\n",tid);
        }
        return 0;
  }
 
  介绍一个获取当前线程id的函数
 
   #include
   pthread_t pthread_self(void);
  获取当前文件中线程tid命令
 
  ps -eLf | head -1&& ps -eLf | grep a.out
  以上都是显示终止,另外还有隐式终止。即:顶层的线程调用返回时,线程会隐式终止。
 
  线程等待
 
  为什么需要线程等待?
 
  已经退出的线程线程池linux,其空间没有被释放,仍然在进程的地址空间内。 创建新的线程不会复?用刚才退出线程的地址空间。
 
  功能:等待线程结束原型
 
  int pthread_join(pthread_t thread, void **value ptr);
 
  参数
 
  thread:线程ID
 
  value_ptr:它指向一个指针,后者指向线程的返回值返回值:成功返回0;失败返回错误码
 
  调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:
 
  如果thread线程通过return返回,value ptr所指向的单元?里存放的是thread线程函数的返回值。
 
  如果thread线程被别的线程调?用pthread cancel异常终掉,value_ ptr所指向的单元里存放的是常数
 
  PTHREAD CANCELED。
 
  如果thread线程是自调用pthreadexit终止的,valueptr所指向的单元存放的是传给pthread_exit的参数。
 
  如果对thread线程的终止状态不感兴趣,可以传NULL给value_ ptr参数。
 
  测试用例可以参考上面的代码。
 
  线程分离
 
  线程分离接口
 
  #include
  int pthread_detach(pthread_t thread);
  #include
  int pthread_detach(pthread_t self());
  返回值:都是成功返回0,失败返回-1.
 
  线程被分离后,就不能在进行pthread_join()操作。否则会出错。因为分离后的线程资源自动就被回收了,再进行等待回收资源。必定导致等待失败。
 
  测试用例如下
 
  #include
  #include
  #include  
  #include
  void * thread_run(  void *arg)
  {
        //新分离线程自我分离
        pthread_detach(pthread_self( ));
        printf("%s\n",(  char *)arg);
        return NULL;
  }
  int  main(  )
  {
        pthread_t tid;
        if(  pthread_create(&tid,NULL,thread_run,"thread1 run...")!=0)
        {
              printf("create thread error\n");
              return 1;
        }
        //主线程分离新线程
        //pthread_detach(tid);
        int ret=0;
        sleep(1);
        if(  pthread_join(  tid,NULL)==0)
        {
              printf("wait thread success\n");
              ret=0;
        }
        else
        {
              printf("wait thread failed\n");
              ret= 1;
        }
        return ret;
  }
 

(编辑:我爱制作网_沈阳站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!