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;
}- main函数开始执行。
- 使用 pthread_create创建了两个线程tid1和tid2。
- th1线程开始执行其无限循环,并在每次迭代中打印 "发送视频",然后暂停一秒。
- 同时(几乎是同时),th2线程也开始执行其无限循环,不断打印 "接受控制"。
- 因为两个线程是并发执行的,所以它们之间没有固定的打印顺序。这取决于操作系统调度器的决策,哪个线程在何时获得CPU时间片。
- 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)