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)

如果上面两个函数不是成对出现,会出现do -while()函数错误。

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)

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

void clean(void* arg)
{
    printf("this is clean ,arg is %s\n",(char*)arg);
    free(arg);
}
void* th(void*arg)
{
    pthread_cleanup_push(clean,arg);
    printf("th arg is %s\n",(char*)arg);

    pthread_cleanup_pop(1);

    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid;
    char* p = (char*)malloc(50);
    strcpy(p,"hello");
    pthread_create(&tid,NULL,th,p);
    pthread_join(tid,NULL);
    return 0;
}


                    
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值