Linux应用——线程(一)

目录

一、线程概述

二、线程创建

三、线程终止

四、连接已终止的线程

 五、线程分离

六、线程取消

七、线程属性 

一、线程概述

        与进程类似,线程是允许应用程序并发执行多任务的一种机制。一个进程可以包含多个线程。同一个程序的所有线程均会独立执行相同的程序,且共享同一份全局内存区域,其中包括初始化数据段,未初始化数据段,以及堆内存段。(传统意义上的UNIX进程只是多线程程序的一个特例,该进程包含一个线程)。

并发与并行:并行需要多个CPU同时进行执行,并发就是多个任务轮流执行。

其实,在Linux底层是将线程当做进程去看。

        进程是CPU分配资源的最小单位,线程是操作系统调度执行的最小单位。

        线程是轻量级的进程LWP,在Linux环境下线程的本质仍然是进程;

        查看进程的LWP号:ps -LF pid

        我们在界面打开一个火狐浏览器,然后通过查看进程,然后查看线程;

                然后我们关闭浏览器,其线程就会消失;

线程与进程的区别:

1、进程间的信息难以共享,由于除去只读代码段位,父子进程并未共享内存,因此必须采用一些进程间通信方式,在进程间进行信息交换。

2、调用fork()来创建进程的代价相对较高,即便是利用写时复制技术,仍需要复制诸如内存页表和文件描述符表之类的多种进程的属性,这意味这fork()调用在时间上的开销依然不菲。

3、线程之间能够方便、快速地共享信息,只需要将数据复制到共享(全局或者堆)变量中即可。

4、创建线程比创建进程通常要快10倍甚至更多,线程间是共享虚拟地址空间的,无需采用写时复制来复制内存,也无需复制页表。

        线程的创建,即便是写时复制,也要重现创建虚拟地址空间的,但是线程的,他们之间相互共享虚拟地址空间。只是在栈空间、text段会进行分开,每个线程都有自己特有的区域。

线程之间共享和非共享资源:

共享资源:

进程ID和父进程ID、进程组ID和会话ID、用户ID和用户组ID、文件描述符表、信号处置、文件系统的相关信息:文件权限掩码、当前工作目录、虚拟地址空间(除栈、.text外);

非共享资源:

线程ID、信号掩码、线程特有数据、error变量、实时调度策略和优先级、栈、本地变量和函数的调用链接信息。 

查看当前 pthread 库版本:getconf GNU_LIBPTHREAD_VERSION

二、线程创建

        一般情况下,main函数所在的线程,我们称之为主线程(main线程),其余创建的线程称之为子线程;

        程序中默认只有一个进程,fork()一次会产生一个子进程。有两个进程;

        程序中默认只有一个线程,pthread_creat()函数调用会创建一个线程,一个主线程一个子线程。

进程创建函数:pthread_create();

函数:

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

涉及的头文件:

         #include <pthread.h>   

功能:

        创建一个子线程;

参数:

            pthread_t *thread:传出参数,线程创建成功后,子线程的ID会被写到改变量中;

            const pthread_attr_t *attr:attr,需要设置线程的属性,一般情况下,NULL;

            void *(*start_routine) (void *):函数指针:这个函数子线程需要处理的逻辑代码;

            void *arg:给第三个参数使用;

返回值:

        成功:返回0;

        错误:返回错误号,这个错误号和之前的error不太一样。

        不能通过perror获取信息错误号,而是利用char *=strerror(int errnum);获得错误原因。

注:连接编译时需要加上-prhread;Compile and link with -pthread;

案例:  

#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void * callback(void * arg)
{
    printf("child thread...\n");
    return NULL;
}

int main()
{
    pthread_t tid;
    int ret = pthread_create(&tid,NULL,callback,NULL);
    if(ret!=0)
    {
        char * errstr=strerror(ret);
        printf("error:%s\n",errstr);
    }

    for(int i=0;i<5;i++)
    {
        printf("%d\n",i);
    }
    return 0;
}

运行结果:      

结果分析:

        在结果中并没有出现我们所创建的线程中的回调函数的内容,这是因为线程创建需要时间,而且主线程可能一直牢牢占取着CPU资源,使得子线程并没有进行运行。

        我们将主线程进行一定的延时;

#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void * callback(void * arg)
{
    printf("child thread...\n");
    return NULL;
}

int main()
{
    pthread_t tid;
    int ret = pthread_create(&tid,NULL,callback,NULL);
    if(ret!=0)
    {
        char * errstr=strerror(ret);
        printf("error:%s\n",errstr);
    }

    for(int i=0;i<5;i++)
    {
        printf("%d\n",i);
    }
    sleep(1);
    return 0;
}

运行结果:   

#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void * callback(void *arg)
{
    printf("child thread...\n");
    printf("arg value :%d\n",*(int *)arg);
    return NULL;
}

int main()
{
    int num=10;
    pthread_t tid;
    int ret = pthread_create(&tid,NULL,callback,(void *)&num);

    if(ret!=0)
    {
        char * errstr=strerror(ret);
        printf("error:%s\n",errstr);
    }

    for(int i=0;i<5;i++)
    {
        printf("%d\n",i);
    }
    sleep(1);
    return 0;
}

运行结果: 

三、线程终止

        线程有创建就会有终止,就如同进程一样,线程也有终止的相关API函数;

函数:

        void pthread_exit(void *retval);   

涉及的头文件:

         #include <pthread.h>

 功能

        终止一个线程,在哪个线程中调用就终止哪个线程。

 参数:

           retval传出参数,需要传递一个指针,最为一个返回值,可以在pthread_join()中获取到,如果不需要传出就传递NULL。        

       Compile and link with -pthread.

函数:

        pthread_t pthread_self(void);

功能:

        获取当前线程的ID;

函数:

         int pthread_equal(pthread_t t1, pthread_t t2);

功能:

        比较两个线程ID是否相等;

        不同操作系统,pthread_t 类型的实现不一致,有的无符号的长整型,有的是使用结构体实现。

 案例:

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

void * callback(void * arg)
{
    printf("child thread id:%ld\n",pthread_self());
    return NULL;

}

int main()
{
    //创建一个子线程:
    pthread_t tid;
   int ret = pthread_create(&tid,NULL,callback,NULL);
    if(ret!=0)
    {
        char * errstr = strerror(ret);
        printf("erroc:%s\n",errstr);
    }
    //主线程
    for(int i=0;i<5;i++)
    {
        printf("i=%d\n",i);
    }
    printf("tid:%ld man thread id:%ld\n",tid,pthread_self());
   //让主线程退出,当主线程退出时,他不会影响其他正常运行的线程;
   pthread_exit(NULL);
   printf("main thread exit\n");
    return 0;
}

  运行结果:

结果分析:

        在主线程中创建了一个子线程,然后执行主线程和子线程的程序,并在主线程和子线程中获取对应的线程ID,在主线程中采用线程退出函数。主线程虽然退出了,但是整个进程并没有结束,等到所有线程都退出后,进程才结束。

四、连接已终止的线程

 涉及头文件 :

#include <pthread.h>

 函数原型:

      int pthread_join(pthread_t thread, void **retval);

  功能:         

        和一个已经终止的线程进行连接。 回收子线程的资源。

        多个子线程结束后如果没被回收,会形成僵尸线程。

        关于进程和线程关于终止进程线程回收的区别:进程只能由父进程进行回收(父进程结束,由进程号为1的进程进行领养),而线程是任意线程都可以回收终止的线程,线程使用的就是pthread_join函数。

        连接的含义就是连接后,对函数进行一个释放。

  特点:

        这个函数是阻塞函数,调用一次只能回收一个子线程。

          一般在主线程中使用。但是在其他线程中也可以使用;

 参数:

             pthread_t thread——需要回收子线程的ID。

             retval:接收子线程退出时的值。

  返回:

            成功返回0;非0失败,返回错误号。

案例:

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
int value=10;
void * callback(void * arg)
{
    printf("child thread id:%ld\n",pthread_self());
   //  sleep(3);
    //return NULL;
  //  int value=10;//局部变量
    pthread_exit((void *)&value);

}

int main()
{
    //创建一个子线程:
    pthread_t tid;
   int ret = pthread_create(&tid,NULL,callback,NULL);
    if(ret!=0)
    {
        char * errstr = strerror(ret);
        printf("erroc:%s\n",errstr);

    }
    //主线程
    for(int i=0;i<5;i++)
    {
        printf("i=%d\n",i);
    }
    printf("tid:%ld man thread id:%ld\n",tid,pthread_self());
    //主线程调用pthread_jion()回收子线程的资源。
    int * thread_retval;
    ret = pthread_join(tid, (void *)&thread_retval);
    if(ret!=0)
    {
        char * errstr = strerror(ret);
        printf("erroc:%s\n",errstr);

    }   
    printf("exit data :%d\n",* thread_retval);
    printf("回收子线程资源成功!\n");
   //让主线程退出,当主线程退出时,他不会影响其他正常运行的线程;
   pthread_exit(NULL);
    return 0;
}

运行结果: 

 五、线程分离

涉及的头文件:        

  #include <pthread.h>

函数原型:

       int pthread_detach(pthread_t thread);

功能:

        分离一个线程,将线程标记为分离线程,在分离线程终止时,它的资源会被自动释放不需要与终止线程再进行连接

         注意:1、一个线程只能分离一次,不能多次分离。会产生不可预料的行为;

                    2、不能去连接一个已经分离的线程;会报错;

参数

        需要分离的线程的ID;

返回值:

        成功返回0;失败返回错误号;

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

void * callback(void * arg)
{
    printf("child thread id:%ld\n",pthread_self());
    return NULL;
}

int main()
{

    //1、创建一个子线程
    pthread_t tid;
   int ret= pthread_create(&tid,NULL,callback,NULL);
    if(ret!=0)
    {
        char * errstr= strerror(ret);
        printf("error1:%s\n",errstr);
    }
//输出主线程和子线程的ID
printf("tid:%ld,main thread id :%ld\n",tid,pthread_self());

//设置子线程分离,子线程分离后,子线程结束时,子线程的资源就不要主线程释放;

    //设置分离后,对子线程进行连接

   ret =  pthread_detach(tid);
    if(ret!=0)
    {
        char * errstr= strerror(ret);
        printf("error2:%s\n",errstr);
    }

   ret = pthread_join(tid,NULL);

    if(ret!=0)
    {
        char * errstr= strerror(ret);
        printf("error3:%s\n",errstr);
    }
    pthread_exit(NULL);
    return 0;

}

运行结果:

        线程分离后不需要在用过其他线程在对其进行一个线程连接,如果再链接就会得到失败,我们案例中检测,返回了失败的原因。 

六、线程取消

涉及头文件:

   #include <pthread.h>

函数原型:

 int pthread_detach(pthread_t thread);

功能:

         取消线程(让线程终止)

         取消某个线程,可以终止某个线程的运行,但不是立马终止;

         而是当子线程执行到一个取消点,线程才会终止,

    取消点:系统规定好的一些系统调用,我们可以粗略的理解为从用户区到内核区的切换。

    案例:

#include <stdio.h>
#include <pthread.h>

void * callback(void * arg)
{
    printf("child thread id:%ld\n",pthread_self());
    for(int i=0;i<5;i++)
    {
        printf("chaild:%d\n",i);
    }
    return NULL;
}


int main()
{

    //1、创建一个子线程
    pthread_t tid;
   int ret= pthread_create(&tid,NULL,callback,NULL);
    //取消线程
    pthread_cancel(tid);

for(int i=0;i<5;i++)
{
    printf("%d\n",i);

}
//输出主线程和子线程的ID
printf("tid:%ld,main thread id :%ld\n",tid,pthread_self());

    pthread_exit(NULL);
    return 0;

}

运行结果: 

每次运行结果是不同的,线程取消不是立刻就取消的,而是到了取消点(printf)才会取消。

七、线程属性 

相关函数:   

      int pthread_arr_init(pthread_attr_t *attr);

        初始化线程属性变量

    int pthread_attr_destroy(pthread_attr_t *attr);

        释放线程属性的资源;

    int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

       获取线程分离的状态属性;

    int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);

      设置线程分离的状态属性

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

void * callback(void * arg)
{
    printf("child thread id:%ld\n",pthread_self());
    return NULL;
}


int main()
{
    //创建一个线程属性变量
    pthread_attr_t attr;
    //初始化属性变量
    pthread_attr_init(&attr);
    //设置属性;
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    //获取线程的栈大小
    size_t  size;
    pthread_attr_getstacksize(&attr,&size);
    printf("%ld\n",size);

    //1、创建一个子线程
    pthread_t tid;
   int ret= pthread_create(&tid,&attr,callback,NULL);
    if(ret!=0)
    {
        char * errstr= strerror(ret);
        printf("error1:%s\n",errstr);
    }
//输出主线程和子线程的ID
printf("tid:%ld,main thread id :%ld\n",tid,pthread_self());

//设置子线程分离,子线程分离后,子线程结束时,子线程的资源就不要主线程释放;
   
//释放线程属性资源
    pthread_attr_destroy(&attr);
    pthread_exit(NULL);
    return 0;

}

 运行结果:

获取线程栈的大小, 创建一个线程属性变量,初始化属性变量,设置属性,获取线程的栈大小。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值