Linux高级编程——线程

pthread 线程
   

    概念 :线程是轻量级进程,一般是一个进程中的多个任务
                进程是系统中最小的资源分配单位.
                线程是系统中最小的执行单位。

  优点: 比多进程节省资源,可以共享变量

进程会占用3g左右的空间,线程只会占用一部分,大概8M的空间

进程的父子不会共享,但一个进程之间的线程的资源可以共享.

进程的父子不是平级关系,线程是平级关系

 特征:s's
    1、共享资源
    2、效率高  30%
    3、三方库: pthread  clone   posix
            3.1 编写代码头文件: pthread.h
            3.2 编译代码加载库: -lpthread   library 
            libpthread.so  (linux库)
            gcc 1.c -lpthread     -lc
    缺点:
    1,线程和进程相比,稳定性,稍微差些
    2,线程的调试gdb,相对麻烦些。
        info thread 
        *1  
        2 
        3
        thread 3 
        
线程与进程区别:

    资源:
        线程比进程多了共享资源。  IPC
        线程又具有部分私有资源。
        进程间只有私有资源没有共享资源。
    空间:
        进程空间独立,不能直接通信。
        线程可以共享空间,可以直接通信。

       进程解决相对复杂的问题,线 程解决相对复杂的问题.

共同点:

二者都可以并发

3、线程的设计框架  posix
    

创建多线程 ==》线程空间操作 ===》线程资源回收
errno   strerror(errno)  perror();
  

 3.1 创建多线程:


    int pthread_create(
        pthread_t *thread const pthread_attr_t *attr,
        void *(*start_routine) (void *), void *arg);
    功能:该函数可以创建指定的一个线程。
    参数:thread 线程id,需要实现定义并由该函数返回。
          attr   线程属性,一般是NULL,表示默认属性。
          start_routine      指向指针函数的函数指针。
                  本质上是一个函数的名称即可。称为
th                回调函数,是线程的执行空间。
{
}
          arg  回调函数的参数,即参数3的指针函数参数。
 

 返回值:成功 0
                失败 错误码

注意:一次pthread_create执行只能创建一个线程。
      每个进程至少有一个线程称为主线程。
      主线程退出则所有创建的子线程都退出。暂时先用while(1); 
      主线程必须有子线程同时运行才算多线程程序。
      线程id是线程的唯一标识,是CPU维护的一组数字。
      pstree 查看系统中多线程的对应关系。
      多个子线程可以执行同一回调函数。
    ps -eLf 查看线程相关信息Low Weigth Process
    ps -eLo pid,ppid,lwp,stat,comm

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
void *th1(void*arg)
{while(1){printf("发送视频\n");sleep(1);}
}void *th2(void*arg)
{while(1){printf("接受控制\n");}
}int main(int argc, const char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);while(1);return 0;
}
  1. main 函数开始执行。
  2. 使用 pthread_create 创建了两个线程 tid1 和 tid2
  3. th1 线程开始执行其无限循环,并在每次迭代中打印 "发送视频",然后暂停一秒。
  4. 同时(几乎是同时),th2 线程也开始执行其无限循环,不断打印 "接受控制"。
  5. 因为两个线程是并发执行的,所以它们之间没有固定的打印顺序。这取决于操作系统调度器的决策,哪个线程在何时获得CPU时间片。
  6. main 函数中的 while(1); 是一个空循环,它使主线程保持活动状态,防止程序立即退出。然而,这个空循环并没有为程序提供任何有用的功能,通常你可能会使用某种形式的线程同步或等待(如 pthread_join)来确保主线程在所有其他线程完成后才退出。

此时输出是乱的,是由于

  • 线程调度是由操作系统控制的,它决定哪个线程在何时运行。这取决于许多因素,包括线程优先级、系统负载、可用的CPU核心数量等。
  • 由于两个线程都在无限循环中,并且没有同步机制(如互斥锁、条件变量等),所以它们会尽可能快地交替执行(或并行执行,如果系统有多个CPU核心),导致输出看起来没有规律。

2、pthread_t pthread_self(void); unsigned long int; %lu  获取线程号


   功能:获取当前线程的线程id
   参数:无
   返回值:成功 返回当前线程的线程id
               失败  -1;
            syscall(SYS_gettid);
这个方法重启后失效
alias gcc='gcc -g -pthread '
unalias gcc 

永久起作用
cd ~ //家目录
vim .bashrc
alias gcc='gcc -g -pthread '  :wq

source .bashrc  生效

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{while(1){printf("发送视频 %lu\n",pthread_self());sleep(1);}
}void *th2 (void*arg)
{while(1){printf("接受控制 %lu\n",pthread_self());sleep(1);}
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);printf("main th %lu\n",pthread_self());while(1);return 0;
}
  • 使用pthread_create创建两个线程:tid1(运行th1)和tid2(运行th2)。
  • 打印主线程的ID。
  • 使用while(1);使主线程进入无限循环,以保持程序运行。否则,当主线程结束时,程序可能会立即终止,导致其他线程也被终止

练习题:
    设计一个多线程程序,至少有三个子线程
    每个线程执行不同的任务,并实时打印执行
    过程,同时表明身份。

    eg: ./a.out  ==>tid =xxx...  zheng ...
                    tid2 = xxx wozai.
                    tid3 = xxx  wozai ssss


线程的退出:

1.直接用return;   

2: 自行退出 ==》自杀  ==》子线程自己退出
        exit(1);
        void pthread_exit(void *retval);  exit  return p;
        功能:子线程自行退出
        参数: retval 线程退出时候的返回状态,临死遗言。
        返回值:无

            th
            {
                int a =10;

                pthread_exit(&a);
            }
            join(,&ret)


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{int i =10;while(i--){printf("发送视频 %lu\n",pthread_self());sleep(1);}pthread_exit(NULL);//return NULL;
}void *th2 (void*arg)
{int i = 10;while(i--){printf("接受控制 %lu\n",pthread_self());sleep(1);}pthread_exit(NULL);
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);printf("main th %lu\n",pthread_self());while(1);return 0;
}


    3. 强制退出 ==》他杀  ==》主线程结束子线程
        int pthread_cancel(pthread_t thread);
        功能:请求结束一个线程  (在主线程种调用 写入某个线程id号,可以关闭该线程)
        参数:thread 请求结束一个线程tid(想要关闭的线程id号)
        返回值:成功 0
                失败 -1;

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{while(1){printf("发送视频\n");sleep(1);}
}void *th2 (void*arg)
{while(1){printf("接受控制\n");sleep(1);}
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);int i = 0 ;while(1){i++;if(3 == i ){pthread_cancel(tid1);}if(5 ==i){pthread_cancel(tid2);}sleep(1);}return 0;
}

作业:
    创建一个多线程程序,至少有10个子线程,
    每个线程有会打印不同的数据,同时表明身份。

    
    线程的回收


    1、线程的回收机制 ====》不同与进程没有孤儿线程和僵尸线程。
                                    ====》主线程结束任意生成的子线程都会结束。
                                      ====》 子线程的结束不会影响主线程的运行。
    char * retval ; retval++; 1 
    int * retval; 

    int pthread_join(pthread_t thread, void **retval);    
  功能:通过该函数可以将指定的线程资源回收,该函数具有阻塞等待功能,如果指定的线程没有结束,则回收线程会阻塞。
  参数:thread  要回收的子线程tid
              retval  要回收的子线程返回值/状态。==》ptread_exit(值);
  返回值:成功 0
                 失败 返回一个错误号,是一个大于零的数;

                  失败可以用

               

 

#include <unistd.h>
#include <string.h>
#include <pthread.h>
void *th1 (void*arg)
{int i = 10;while(i--){printf("发送视频\n");sleep(1);}
}void *th2 (void*arg)
{int i = 10;while(i--){printf("接受控制\n");sleep(1);}
}int main(int argc, char *argv[])
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);int ret = pthread_create(&tid2,NULL,th2,NULL);if(ret!=0){// perror()fprintf(stderr,"error %s\n",strerror(ret));//exit();}pthread_join(tid1,NULL);pthread_join(tid2,NULL);return 0;
}

子线程的回收策略:
  1、如果预估子线程可以有限范围内结束则正常用pthread_join等待回收。
  2、如果预估子线程可能休眠或者阻塞则等待一定时间后强制回收。
  3、如果子线程已知必须长时间运行则,不再回收其资源。
  
  


  线程的参数,返回值
  

1、传参数
        
    传整数 ===》int add(int a,int b);  ///a b 形参
                add(x,y);    x y 实参 

        pthread_create(&tid,NULL,fun,x);

        fun ==>void * fun(void * arg);
        
    练习:创建一个子线程并向该线程中传入一个字符在
          线程中打印输出。
          在此基础上向子线程中传入一个字符串,并在
          子线程中打印输出。

          
          add(int a, int b)
          {
            int c = a+b;
            char buf[]=""
            return c;
          }
                    5
          int d = add(2,3);

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* th(void* arg)
{static int a =20;return &a;
}int main(int argc, char *argv[])
{pthread_t tid;void* ret;pthread_create(&tid,NULL,th,NULL);pthread_join(tid,&ret);printf("ret %d\n",*(int*)ret);return 0;
}


    传字符串
        栈区字符数组:
        字符串常量:
        char *p = "hello";
        堆区字符串;
            char *pc = (char *)malloc(128);
            ptread_create(&tid,NULL,fun,pc);

            pthread_join(tid,NULL);

            free(pc);
        
            fun(void *arg)
            {
                char * pc = (char *)arg    ;
                printf("%s \n",pc);
                        %c
            }

栈区

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* th(void* arg)
{static char buf[256]={0};strcpy(buf,"要消亡了\n");return buf;
}int main(int argc, char *argv[])
{pthread_t tid;void* ret;pthread_create(&tid,NULL,th,NULL);pthread_join(tid,&ret);printf("ret %s\n",(char*)ret);return 0;
}

堆区:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* th(void* arg)
{char * tmp = (char* )arg;strcpy(tmp,"hello");return tmp;
}int main(int argc, char *argv[])
{pthread_t tid;char * p = (char*)malloc(50);void* ret;pthread_create(&tid,NULL,th,p);pthread_join(tid,&ret);printf("ret %s\n",(char*)ret);free(p);return 0;
}

  

 传结构体
    1、定义结构体类型
    2、用结构体定义变量
    3、向pthread_create传结构体变量
    4、从fun子线程中获取结构体数据

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
typedef struct 
{char * p;int a;
}TH_ARG;
void* th(void* arg)
{TH_ARG * tmp = (TH_ARG* )arg;strcpy(tmp->p,"hello");//strcpy( ((TH_ARG*)arg)->p ,"hello");tmp->a +=10;return tmp;
}int main(int argc, char *argv[])
{pthread_t tid;int a  =20;char * p = (char*)malloc(50);TH_ARG arg;arg.a = a;arg.p = p;void* ret;pthread_create(&tid,NULL,th,&arg);pthread_join(tid,&ret);printf("ret %s %d\n",((TH_ARG*)ret)->p,((TH_ARG*)ret)->a);free(p);return 0;
}

练习:
    定义一个包含不同数据类型的测试结构体
    并向子线程传参数,同时在子线程中打印输出。


    定义一个回调函数可以完成计算器的功能
    定义一个数据结构体可以一次传入不同的数据
    和计算方式并将结果打印输出。
    //2 + 3.6 
    // 2 + 3  2+3
    // 8 * 6
    typedef strcut
    {
        float a;
        float b;
        char c;//+ - * / 
        float d;
    }JSQ;
    
    

返回值:pthread_exit(0) ===>pthread_exit(9);
        pthread_join(tid,NULL); ===>pthread_join(tid,?);
10;
-10;
int * p =malloc(4);
*p = -10;
1、pthread_exit(?) ==>? = void * retval;
                          纯地址

2、pthread_join(tid,?) ==>? = void **retval;
                            地址的地址
原理:子线程退出的时候,可以返回一个内存地址
      改值所在的内存中可以存储任何数据,只要
      地址存在,则数据都可以正常返回。
    
    地址有三种:
    0、栈区变量  错误,子线程结束该地址失效。
    1、全局变量  失去意义,本质可以直接访问。

    2、静态变量 
    3、堆区变量


      主线程通过一个地址形式的变量来接受子进程
      返回的地址变量就可以将该地址中的数据取到。

    练习:从子线程中申请一块堆区内存并存字符串
      将该字符串以返回值形式返回到主线程并打印输出。
          
 

   设置分离属性,目的线程消亡,自动回收空间。
  

主线程没有空,才设置分离属性来回收.

 attribute

 int pthread_attr_init(pthread_attr_t *attr);
    功能,初始化一个attr的变量
    参数:attr,需要变量来接受初始值
    返回:0  成功,
    非0 错误;
       int pthread_attr_destroy(pthread_attr_t *attr);
      功能:销毁attr变量。
      attr,属性变量
      返回:0  成功,
    非0 错误;
       
       
    man -k 
     int pthread_attr_setdetachstate(pthread_attr_t *attr
, int detachstate);

    功能:把一个线程设置成相应的属性
    参数,attr,属性变量,有init函数初始化他。
    detachstate:有2个可选值,
    
    PTHREAD_CREATE_DETACHED:设置分离属性。
    
    第二种设置分离属性:
int pthread_deatch(pthread_t thread);
    功能,设置分离属性
    参数,线程id号,填自己的id
    
    do{
    
    
    }while()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void* th(void* arg)
{pthread_detach(pthread_self());return NULL;
}int main(int argc, char *argv[])
{pthread_t tid;int i = 0 ;for(i=0;i<50000;i++){int ret = pthread_create(&tid,NULL,th,NULL);if(ret!=0){break;}// pthread_detach(tid);}printf("%d \n",i);return 0;
}


void pthread_cleanup_push(void (*routine)(void *), void *arg);

    功能:注册一个线程清理函数
    参数,routine,线程清理函数的入口
        arg,清理函数的参数。
    返回值,无
        
void pthread_cleanup_pop(int execute);
    功能:调用清理函数
    execute,非0  执行清理函数
            0 ,不执行清理
            
    返回值,无

do
{

}while(1)

process                thread
fork                pthread_create 
getpid,ppid,        pthread_self
exit,                pthread_exit 
wait,waitpid,        pthread_join 
kill,                pthread_cancel
atexit                 pthread_clean,
exec                system--->fork->exec (ls)
                    
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/37182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【高考】选专业时,应避免的误区

【高考】选专业时&#xff0c;应避免的误区-CSDN博客 【高考】选专业时以什么为主&#xff1f;-CSDN博客 分数限制下&#xff0c;选好专业还是选好学校&#xff1f;-CSDN博客 分数限制下&#xff0c;选好专业还是选好学校&#xff1f;-CSDN博客 在选专业时&#xff0c;考生…

解析 ThreadLocal 原理

ThreadLocal用于线程局部变量的一个工具类。 原理是为每个线程创建独立的变量副本&#xff0c;从而实现线程数据的隔离。具体来说&#xff0c;ThreadLocal 通过一个 ThreadLocalMap来实现&#xff0c;这个 ThreadLocalMap 是一个自定义的哈希表&#xff0c;用于存储线程和对应的…

Qt creator实现一个简单计算器

目录 1 界面设计 2 思路简介 3 代码 目录 1 界面设计 ​2 思路简介 3 代码 3.1 widget.h 3.2 widget.c 4 完整代码 在这里主要记载了如何使用Qt creator完成一个计算器的功能。该计算器可以实现正常的加减乘除以及括号操作&#xff0c;能实现简单的计算器功能。 1 界…

Hadoop版本演变、分布式集群搭建

Hadoop版本演变历史 Hadoop发行版非常的多&#xff0c;有华为发行版、Intel发行版、Cloudera Hadoop(CDH)、Hortonworks Hadoop(HDP)&#xff0c;这些发行版都是基于Apache Hadoop衍生出来的。 目前Hadoop经历了三个大的版本。 hadoop1.x&#xff1a;HDFSMapReduce hadoop2.x…

MySQL学习_python操作MySQL

用python连接数据库分为以下几个步骤 1.首先下载pymysql pip install pymysql2.创建数据 # 1.导入pymysql import pymysql # 2.连接MySQL conn pymysql.connect(host127.0.0.1,port3306,userroot,charsetutf8,dbunicom) cursor conn.cursor(cursorpymysql.cursors.DicCurso…

uniapp开发企业微信内部应用

最近一直忙着开发项目&#xff0c;终于1.0版本开发完成&#xff0c;抽时间自己总结下在项目开发中遇到的技术点。此次项目属于自研产品&#xff0c;公司扩展业务&#xff0c;需要在企业微信中开发内部应用。因为工作中使用的是钉钉&#xff0c;很少使用企业微信&#xff0c;对于…

重新记录做事的方向和内容(2024年6月28日19:50:38)

感觉自己没必要这么焦虑&#xff0c;最后的结果无非就是自己又开始恢复到自己抽烟&#xff0c;喝酒&#xff0c;说脏话的一个状态&#xff0c;自己那么糟糕自己都已经通过实事求是走出来了&#xff0c;现在难道自己还害怕什么&#xff1f; 如果顺着这种封闭和没有断舍离的状态…

【Qt C++实现绘制仪表盘】

要在Qt C中绘制仪表盘&#xff0c;您可以使用QChart、QSeries、QBarSeries、QPointSeries等类。以下是一个简单的示例&#xff0c;演示如何使用这些类创建一个绘图仪表盘&#xff1a; #include <QApplication> #include <QChart> #include <QChartView> #in…

06 Shell编程实战——案例1

脚本编程步骤&#xff1a; 脚本编程一般分为4个步骤&#xff0c;即先确定需求&#xff0c;然后再确定你所要用到的语句&#xff0c; 需求分析&#xff1a;根据系统管理的需求&#xff0c;分析脚本要实现的功能、功能实现的层次、实现的命令与语句等&#xff1b;命令测试&…

Windows11下安装多个JDK版本,并切换

Windows11下安装多个JDK版本,并切换 前言步骤1、前期准备2、版本切换思考前言 一台电脑可以同时安装多个版本 jdk,建议两个,最多不超三个。安装多个JDK版本可能会占用较多的磁盘空间。此外,同时运行多个 JDK 版本可能会对系统性能产生一定的影响。   切换 JDK 有两种方式…

ios swift5 视频播放 播放视频失败 无法播放HEVC (H.265) 格式的视频 H.264格式的可以播放

文章目录 1.问题2.原因&#xff1a;iOS swift AVPlayerViewController无法播放HEVC (H.265) 格式的视频3.解决方法用第三方框架MobileVLCKit来播放4.用MobileVLCKit写的播放器4.1 两个oc版本的4.2 两个swiftUI版本的5.苹果是支持HEVC (H.265) 格式的视频&#xff0c;是硬件那边…

css做旋转星球可举一反三

<!DOCTYPE html> <html lang"en"><head> <meta charset"UTF-8" /> <title>旋转的星球</title> <style type"text/css">.box {/*position: relative;*/position: absolute;width: 139px;height: 139p…

计算文本相似度的几种方法

计算文本相似度的几种方法 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们来探讨一下计算文本相似度的几种方法。文本相似度在自然语言处理&#xff08…

算法训练 | 动态规划Part10 | 300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组

目录 300.最长递增子序列 动态规划法 674.最长连续递增序列 动态规划法 718.最长重复子数组 动态规划法 300.最长递增子序列 题目链接&#xff1a;300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; 文章讲解&#xff1a;代码随想录 动态规划法 “子序列是…

基于java语言+springboot技术架构开发的 互联网智能3D导诊系统源码支持微信小程序、APP 医院AI智能导诊系统源码

基于java语言springboot技术架构开发的 互联网智能3D导诊系统源码支持微信小程序、APP 医院AI智能导诊系统源码 一、智慧导诊系统开发原理 导诊系统从原理上大致可分为基于规则模板和基于数据模型两类。 1、基于规则推理的方法通过人工建立症状、疾病和科室之间的对应规则实现…

Java反射API详解与应用场景

一、Java反射API简介: 一、什么是反射: 反射是一种强大的工具,它允许我们在运行时检查类、方法和字段的信息,甚至允许我们动态的调用特定类的方法或改变字段的值。编程语言中的反射机制通常用于从类、对象或方法中检索元数据,或者更特别的说,从代码本身中获取信息。这就…

【51单片机入门】点亮数码管

文章目录 前言仿真图如何去绘制一个数字示例代码选择某个数码管显示某个数字 示例代码总结 前言 在嵌入式系统的世界中&#xff0c;单片机扮演着至关重要的角色。51单片机&#xff0c;作为最早的微控制器之一&#xff0c;至今仍被广泛应用在各种设备中。本文将介绍如何使用51单…

几种linux开机自启脚本的方法

几种linux开机自启脚本的方法 1. 脚本添加到init.d目录中2. 创建服务service&#xff08;推荐&#xff09;3. /etc/profile & /etc/profile.d&#xff08;不推荐&#xff09;4. /etc/rc.local 本文以启动jenkins节点为例&#xff0c;需要持久连接&#xff0c;实现开机自启 …

js或ts中对象如何循环遍历获取名字和值

数组循环有多种方法&#xff0c;但是对象循环还是会遇到一些问题 分开获取key或value let names{name:kaka,age:12}获取key值代码&#xff1a; Object.keys(names).forEach(name>{console.log(name) })结果&#xff1a; 获取value值代码&#xff1a; Object.values(name…

多地高温持续“热力”爆表 约克VRF中央空调带你清凉舒爽一夏

“出门5分钟&#xff0c;流汗2小时”,夏季高温天气&#xff0c;怎一个“热”字了得&#xff1f;6月以来&#xff0c;我国多地迎来高温“炙烤”&#xff0c;全国出现40℃以上高温的范围持续增加&#xff0c;随着中央气象台高温预警持续拉响&#xff0c;人们都很纳闷&#xff1a;…