Linux系统编程————线程及其相关函数

一、线程相关概念:

什么是线程:

        1>线程就是进程的一条执行路径,每一个进程都有一条线程,称之为主线程。

        2>在Linux系统下:线程就是轻量级的进程;

        3>在Linux内核看来,线程就是进程;因为linux系统早期是没有线程概念的,只有pcb表示的进程,而在后来发展中,程序员用进程的方式封装了线程,所以每个线程都有独立的pcb,所以在Linux内核中是分不清进程和线程的(一律当进程处理);

注意事项:

        1>进程在创建线程之后,地址空间没有发生改变,但自己退化成一条线程(主线程);

        2>同一进程创建出的子线程和主线程共用一个地址空间,但是每个线程都有自己独立的pcb,子线程的pcb是从主线程拷贝而来;

        3>同一进程产生的所有线程 共享的是: 虚拟地址空间的.text段、.bass段、.data段、堆、动态库加载区、环境变量、命令行参数; 不共享的是:

        4>地址空间的栈区会被n个线程平均分成n份,每个线程都独有自己的一份栈区。 

线程与进程的区别:

        1>进程是一个正在运行的程序,是系统进行资源分配的基本单位;

        2>线程是进程内部的一条执行路径,是系统调度的基本单位;

        3>线程的创建、管理等代价比进程小。

二、线程创建:pthread_create

创建线程的函数原型:

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

        引用头文件:#include,线程相关的这些函数都需要调用这个头文件
参数:
        thread:传出参数,线程创建成功之后,会被设置一个合适的值;
        attr:默认传NULL;
        start_routine:函数指针,回调函数(指向子线程的处理函数);
        arg:处理函数的参数
返回值:
         ==0: 创建成功
         !=0: 创建失败,返回错误号,可以调用char* strerror(int errnum)函数打印错误信息。

例1、 创建五个线程,打印出他们是第几个被创建出来的

#include                                                                                                                                                               
#include
#include


//子线程的回调函数
void* show(void* arg)
{
    //取参数
    //int num = *((int*)arg);//取得是地址
    int num = (int)arg;//取得是值
    printf("第%d个创建的子线程:%lu\n",num,pthread_self());
 
    return NULL;
}

int main()
{
    int n = 1;
    pthread_t pthid[5];//线程id
    while (n < 6)
    {
        //第四个参数是给回调函数传的参数,传的是地址;在这个地方传地址会出现问题,因为五个进程的地址都指向同一块空间
        //pthread_create(&pthid[n],NULL,show,(void*)&n);//创建线程
        //给第四个参数传值
        pthread_create(&pthid[n],NULL,show,(void*)n);//创建线程
       n++;
    }
    printf("parent thread id:%lu\n",pthread_self());//打印主线程id
 
    sleep(3);
    return 0;
}  

三、线程退出:pthread_exit

线程退出的函数原型: void pthread_exit(void* retval);

参数: retval指针:必须指向全局/堆的数据;

作用: 让此线程退出不影响其他线程。

例2、 使用pthread_exit函数,让子线程退出不影响主线程。

 15 //子线程的回调函数
 16 void* show(void* arg)
 17 {
 18     printf("child thread id:%lu\n",pthread_self());
 19     int i = 0;
 20     while(i < 5)
 21     {
 22         printf("chile i:%d\n",i++);
 23         if(i == 2)
 24         {
 25             //exit(1);//会影响主线程
 26             pthread_exit(NULL);
 27         }
 28     }
 29     //pthread_exit(NULL);
 30 
 31     return NULL;
 32 }
 33 
 34 int main()
 35 {
 36     pthread_t pthid;//线程id
 37     int ret = pthread_create(&pthid,NULL,show,NULL);//创建线程
 38     if(ret != 0)
 39     {
 40         printf("error num:%d,%s\n",ret,strerror(ret));//打印错误信息
 41     }
 42 
 43     printf("parent thread id:%lu\n",pthread_self());//打印主线程id
 44 
 45     //pthread_exit(NULL);//退出主线程,此行之后的代码就不会被执行
 46     int i = 1;
 47     sleep(2);
 48     while(i)
 49     {
 50         printf("parent i:%d\n",i++);
 51     }
 52 
 53     pthread_exit(NULL);
 54     return 0;
 55 }   

四、线程回收:pthread_join

线程回收的函数原型:

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

参数:

        pthid:要回收的子线程id;

        retval二级指针:读取线程退出时携带的状态信息;必须指向全局或堆上的内存是一个传出参数,指向的内存和pthread_exit参数指向同一块内存地址。

作用:

         阻塞等待子线程退出,因为子线程和子进程一样,都无法回收自己的pcb。

例3、 使用pthread_join函数,让主线程阻塞等待回收子线程。

 17 //子线程的回调函数
 18 void* show(void* arg)
 19 {
 20     printf("child thread id:%lu\n",pthread_self());
 21     int i = 0;
 22     while(i < 5)
 23     {
 24         printf("child i:%d\n",i++);
 30     }
 32 
 33     return NULL;
 34 }
 35 
 36 int main()
 37 {
 38     pthread_t pthid;//线程id
 39     int ret = pthread_create(&pthid,NULL,show,NULL);//创建线程
 40     if(ret != 0)
 41     {
 42         printf("error num:%d,%s\n",ret,strerror(ret));//打印错误信息
 43     }
 44 
 45     printf("parent thread id:%lu\n",pthread_self());//打印主线程id
 46 
 47     pthread_join(pthid,NULL);//如果想获取子线程的退出信息,则定义一个指针传入NULL的位置。
 48     int i = 0;
 49     while(i < 10)
 50     {
 51         printf("parent i:%d\n",i++);
 52     }
 53 
 54     //pthread_exit(NULL);
 55     return 0;
 56 }    

五、线程分离:pthread_detach

线程分离的函数原型:

        void pthread_detach(pthread_t pthid);

参数:

        pthid:要和主线程分离的子线程id;

作用:

        1>调用线程分离函数之后,子线程就可以自己回收自己的pcb;

        2>调用线程分离函数后就不需要调用pthread_join()函数了。

例4、 使用pthread_detach函数,让主线程与子线程分离。

17 //子线程的回调函数
 18 void* show(void* arg)
 19 {
 20     printf("child thread id:%lu\n",pthread_self());
 21     int i = 0;
 22     while(i < 5)
 23     {
 24         printf("chile i:%d\n",i++);
 25     }
 26     pthread_exit(NULL);
 27 
 28     return NULL;
 29 }
 30 
 31 int main()
 32 {
 33     pthread_t pthid;//线程id
 34     int ret = pthread_create(&pthid,NULL,show,NULL);//创建线程
 35     if(ret != 0)
 36     {
 37         printf("error num:%d,%s\n",ret,strerror(ret));//打印错误信息
 38     }
 39 
 40     printf("parent thread id:%lu\n",pthread_self());//打印主线程id
 41     pthread_detach(pthid);//将子线程与主线程分离
 42     
 43     int i = 0;
 44     while(i < 10)
 45     {
 46         printf("parent i:%d\n",i++);
 47     }
 48 
 49     pthread_exit(NULL);
 50     return 0;
 51 }                         

六、线程取消:pthread_cancel

线程取消的函数原型:

        void pthread_cancel(pthread_t pthid);

参数: pthid:对应子线程的线程id;

作用: 杀死对应线程id的子线程。

注意事项:

        1>并不一定调用此函数就一定能杀死子线程;

        2>将要被杀死的子线程的处理函数内部必须有一个取消点,即系统调用(间接调用也算,比如printf函数),才能成功杀死此子线程。

        3>pthread_testcancle()函数:是一个系统调用,作为一个取消点存在,没有实际意义。

        4>如果想让主线调用pthread_cancle杀死子线程,但是不知道子线程处理函数中没有使用系统调用,则子线程处理函数中调用pthread_testcancle()即可;

例5、 使用pthread_cancel函数,让主线程杀死子线程。

 20 //子线程的回调函数
 21 void* show(void* arg)
 22 {
 23     int i = 0;
 24     while(i < 5)
 25     {
 26         if(i == 3)
 27         {
 28             printf("将要被杀死了,child_id=%lu,i=%d\n",pthread_self(),i);
 29         }
 30         i++;
 31     }
 32 
 33     return NULL;
 34 }
 35 
 36 int main()
 37 {
 38     pthread_t pthid;//线程id
 39     int ret = pthread_create(&pthid,NULL,show,NULL);//创建线程
 40     if(ret != 0)
 41     {
 42         printf("error num:%d,%s\n",ret,strerror(ret));//打印错误信息
 43     }
 44     
 45     pthread_cancel(pthid);//杀死子线程
 46     printf("parent thread id:%lu\n",pthread_self());//打印主线程id
 47     
 48     int i = 0;
 49     while(i < 10)
 50     {
 51         printf("parent i:%d\n",i++);
 52     }
 53 
 54     pthread_exit(NULL);
 55     return 0;
 56 }   

七、线程比较:pthread_equal

线程比较的函数原型:

        void pthread_equal(pthread_t t1,pthread_t t2);

作用:

        比较两个线程id是否相同

这是一个预留函数,我们现在的线程id都是无符号的长整型,直接数值比较就可以,没必要用这个函数。

你可能感兴趣的:(Linux)