一.线程的概念

    (1)线程是能够独立调度和分派的基本单位;

    (2)线程中的实体基本上不拥有系统资源,只是有一点必不可少的,能保证其独立运行的资源;

    (3)在一个进程中的多个线程之间可并发执行;同样,不同进程中的线程也能并发执行。

    (4)在同一进程中的各个线程都可以共享该进程所拥有的资源,这表现在所有线程都具有与相同的地址空间。

    因此,Text Segment,Datd Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

    a.文件描述符表

    b.每种信号的处理方式(SIG_IGN,SIG_DFL或者自定义的信号处理函数)

    c.当前工作目录

    d.用户id和组id

    但有些资源是每个线程私有的:

    a.线程id

    b.上下文,包括各种寄存器的值,程序计数器和栈指针

    c.栈空间

    d.errno变量

    e.信号屏蔽字

    f.调度优先级

    将要学习的线程库函数是由POSIX标准定义的,称为POSIX thread或pthread,在Linux上线程函数位于libpthread共享库中,因此在编译时要加上-lpthread选项。

二.线程控制

    (1)创建线程

    #include<pthread.h>

    int pthread_create(pthread_t *thread,const pthread_attr_t *attr,

                   void *(start_routine)(void *),void *arg);

    返回值:成功返回0,失败返回错误号。

    以前学过的系统函数都是成功返回0,失败返回-1;而错误号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号。虽然每个线程也都有一个errno,但这是为了兼容其他函数接口而提供的,Pthread库本身并不使用它,通过返回值返回错误码更加清晰。

    在一个线程中调用pthread_create()创建新的线程之后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。start_routine函数接收一个参数,是通过pthread_create的arg参数传递给它的,该参数的类型为void*,这个指针类型按什么解释根据调用者自己定义。start_routine的返回值类型也是void*,这个指针的含义也由调用者自己定义。start_routine返回时,这个线程就退出了,其他线程可以调用pthread_join得到start_routine的返回值。

    pthread_create成功返回后,新创建的线程的id被填写到thread参数所指向的内存单元,进程id的类型是pid_t,每个进程的id在系统中是惟一的,调用getpid(2)可以获得当前进程的id,是一个正整数值。线程id 的类型是thread_t,它只在当前进程中保证是惟一的,在不同的系统中thread_t这个类型有不同的实现,它可能是一个整数值,也可能是一个结构体,也可能是一个地址,因此不能用printf打印,调用pthread_self(3)可以获得当前线程的id。

    attr参数表示线程属性,我们默认为NULL,表示线程属性取缺省值。

    代码实现如下:

  1 #include<stdio.h>

  2 #include<pthread.h>

  3 #include<unistd.h>

  4 #include<sys/types.h>

  5 

  6 void *thread_run(void *arg)

  7 {

  8         int count=3;

  9         printf("this is thread. pid is:%d,pthread id:%u\n",(int)getpid(),pth    read_self());

 10         return ((void*)5);

 11 }

 12 int main()

 13 {

 14         pthread_t tid;

 15         int ret=pthread_create(&tid,NULL,thread_run,NULL);

 16         if(ret!=0)

 17         {

 18                 printf("create pthread fail.info is:%s\n",strerror(ret));

 19                 return -1;

 20         }

 21         sleep(3);

 22         printf("this is main thread. pid is:%d,pthread id:%u\n",(int )getpid    (),pthread_self());

 23         return 0;

 24 }

运行结果:

wKiom1cV8cXRlMOOAAAr62lHQDk392.png

三.终止线程

    如果只需要终止某个线程而不是终止整个进程,有三种办法:

    (1)从线程函数return;这种方法对主线程不适用,从main函数return相当于调用exit;

    (2)一个线程可以调用pthread_cancel终止同一进程中的另一个线程;

    (3)线程可以调用pthread_exit终止自己。

注意:pthread_exit 或return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配。因为当其他线程得到这个返回指针时线程函数已经退出了。

四.线程等待

    函数原型:int pthread_join(pthread_t thread,void **retval);

    返回值:成功返回0,失败返回错误号。

    调用该函数的线程将挂起等待,直到id为thread的线程终止。thread以不同的方式终止,通过pthread_join()得到的终止状态是不同的,总结如下:

    (1)如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。

    (2)如果thread线程被别的线程调用pthread_cancel异常终止,value_ptr所指向的单元里存放的是PTHREAD_CANCELED。

    (3)如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元里存放的是传给pthread_exit的参数。

    实现代码如下:

  1 #include<stdio.h>

  2 #include<pthread.h>

  3 void* thread1(void *arg)

  4 {

  5         printf("this is thread1.\n");

  6         return ((void*)4);

  7 }

  8 void *thread2(void *arg)

  9 {

 10         printf("this is thread2.\n");

 11         pthread_exit((void*)5);

 12 }

 13 void *thread3(void *arg)

 14 {

 15         while(1)

 16         {

 17                 printf("this is thread3.\n");

 18                 sleep(1);

 19         }

 20 }

 21 int main()

 22 {

 23         pthread_t tid1,tid2,tid3;

 24         pthread_create(&tid1,NULL,thread1,(void*)1);

 25         pthread_create(&tid2,NULL,thread2,(void*)2);

 26         pthread_create(&tid3,NULL,thread3,(void*)3);

 27         sleep(3);

 28         void *ret=NULL;

 29         pthread_join(tid1,&ret);

 30         printf("thread1 id is:%u,,,tid1 return :%d\n",tid1,(int)ret);

 31         pthread_join(tid2,&ret);

 32         printf("thread2 id is:%u,,,tid2 return :%d\n",tid2,(int)ret);

 33         pthread_cancel(tid3);

 34         pthread_join(tid3,&ret);

 35         printf("thread3 id is:%u,,,tid3 return :%d\n",tid3,(int)ret);

 36         return 0;

 37 }

运行结果:

wKiom1cWDh_A10SrAAAxytJyqXk360.png

五.线程分离

    在任何一个时间点上,线程是可结合的或者是分离的。一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源是不释放的。相反,一个分离的线程是不能被其他线程收回和杀死的,它的存储器资源在它终止时由系统自动释放。

    默认情况下,线程被创建成可结合的。为了避免存储器泄露,每个可结合线程都应该要么被显示的回收,即调用pthread_join;要么通过调用函数pthread_detach被分离。

    如果一个可结合线程运行结束但没有被join,则它的状态类似于进程中的僵死状态,即还有一部分资源没有被回收,所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出码,回收其资源。

    由于调用pthread_join后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。这时可以在子线程中加入代码:pthread_detach(pthread_self());或者父线程调用:pthread_detach(thread_id);这样可以将该子线程的状态设置为分离的,在该线程运行结束后会自动释放所有的资源。

   

 代码如下:

    

  1 #include<stdio.h>

  2 #include<pthread.h>

  3 #include<unistd.h>

  4 #include<sys/types.h>

  5 

  6 void *thread_run(void *arg)

  7 {

  8         int count=2;

  9         while(count-->0)

 10         {

 11                 pthread_detach(pthread_self());

 12                 printf("this is thread. pid is:%d,pthread id:%u\n",(int)getp    id(),pthread_self());

 13         }

 14         pthread_exit((void*)2);

 15 }

 16 int main()

 17 {

 18         pthread_t tid;

 19         int ret=pthread_create(&tid,NULL,thread_run,NULL);

 20         int count=3;

 21         while(count-->0)

 22         {

 23                 printf("this is main thread. pid is:%d,pthread id:%u\n",(int            )getpid(),pthread_self());

 24                 sleep(1);

 25         }

 26         sleep(3);

 27         pthread_cancel(tid);

 28         //pthread_detach(tid);  

 29         void *status=0;

 30         int join=pthread_join(tid,&status);

 31         if(join==0)

 32         {

 33                 printf("pthread_join success\n");

 34         }

 35         else

 36         {

 37                 printf("pthread_join failed\n");

 38         }

 39         return 0;

 40 }

   运行结果:

wKioL1cWFYyz89tNAAAroKqaZc8971.png

当使用pthread_detach把线程的状态设置为分离的,那么线程在等待时就会失败。