线程的概念:线程是运行在进程内的一个基本执行流,和进程共享地址空间及资源(类似于父子进程共享地址空间),但每个也有自己的私有资源。


进程强调独占性

  每个进程都有它独立的地址空间,包括Text Segment、Data Segment等

线程强调共享性

  线程的共享资源:

      1.进程代码段

                2.进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)

                3.进程打开的文件描述符

                4.信号的处理器

                5.进程的当前目录和进程用户ID与进程组ID

    线程的私有资源:(线程实现并发性)

      1.线程ID

         每个线程都有自己的线程ID,这个ID在本进程中是唯一的。进程用此来标识线程。   

      2.寄存器组的值

         由于线程间是并发运行的,每个线程有自己不同的运行线索,当从一个线程切换到另一个线程上时,必须将原有的线程的寄存器集合的状态保存,以便将来该线程在被重新切换到时能得以恢复。   

                3.线程的堆栈

        堆栈是保证线程独立运行所必须的。

        线程函数可以调用函数,而被调用函数中又是可以层层嵌套的,所以线程必须拥有自己的函数堆栈,使得函 数调用可以正常执行,不受其他线程的影响。

     4.错误返回码

        由于同一个进程中有很多个线程在同时运行,可能某个线程进行系统调用后设置了errno值,而在该线程还  没有处理这个错误,另外一个线程就在此时被调度器投入运行,这样错误值就有可能被修改。

         所以,不同的线程应该拥有自己的错误返回码变量。

     5.线程的信号屏蔽码

        由于每个线程所感兴趣的信号不同,所以线程的信号屏蔽码应该由线程自己管理。但所有的线程都共享同样  的信号处理器。

     6.线程的优先级

       由于线程需要像进程那样能够被调度,那么就必须要有可供调度使用的参数,这个参数就是线程的优先级。


线程控制

一、函数的原型

  线程的创建

   原  型:pthread_t pthread_create(pthread_t *thread,const pthread_attr_t *attr,

                                                                                   void *(start routine)(void *),void *arg);   

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

  参   数:thread:创建成功后其指向的内存存放线程id

                attr:线程属性,可设为NULL

                start routine:是一个函数指针,指向该线程所要函数的地址

                arg:第三个参数中所要传递的参数,如果参数不止一个可以传一个结构体                                              

    线程的等待

    原  型:int pthread_join(thread_t thread,void **retval);

            返回值:成功返回0,失败返回错误码,对分离状态的线程进行pthread_join的调用会产生失败。                           返回EINVAL。

             参   数:

                           thread:线程id

                           retval:用来存储被等待线程的返回值

             功  能: 以阻塞的方式等待thread指定的线程结束,当函数返回时,被等待线程的资源被收回。

                           如果线程已经结束,该函数会立即返回

                            并且thread指定的线程必须是joinable(可结合)的。


    线程的终止和取消

      由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的 资源并不会随着线程的终止而得到释放,但是可以用pthread_join()函数来同步并释放资源。

       void pthread_exit(void* revtal);

       int pthread_cancel(pthread_t thread); 成功返回0,失败返回错误编号

  

     三种方法

     1.一个线程完成它的任务后直接从线程函数return(下面thread_run1())

     2.执行完自己的代码后调用pthread_exit()自己终止(下面thread_run2())

     3.让同一进程中别的线程来取消自己,调用pthread_cancle(void *);(下面thread_run3())

      注:exit和_exit是退出当前进程,而pthread_exit是退出当前线程。


               线程取消的方法是向目标线程发Cancel信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或 者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。pthread_cancle只是向目标线程发送一个取消请求,之后函数立即返回。而目标线程在取消请求发出以后还是继续运行,直到线程到达某个取消点。取消点是线程检查自身是否被取消,并按照可取消状态进行动作的一个位置。

         可取消状态:线程启动时的默认可取消状态是PTHREAD_CANCEL_ENABLE。 PTHREAD_CANCEL_ENABLE: 表示允许取消。PTHREAD_CANCEL_DISABLE:即不允许取消(收到的取消请求将被忽略)

         


   原 型:pthread_t pthread_self();

   返回值:总是成功的,返回调用它的线程id


在编译时注意加上-lpthread参数,以调用链接库。因为pthread并非Linux系统的默认库,而是posix线程库,在Linux中将其作为一个库来使用,因此加上 -lpthread(或-pthread)以显示的链接该库。函数在执行错误时的错误信息将作为返回值返回,并不修改系统全局变量errno,当然也无法使用perror()打印错误信息。

二、代码实现

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

void* thread_run1(void *arg)
{
    printf("this is thread%d:pid:%d,tid:%u\n",(int)arg,getpid(),pthread_self());
    return (void*)0;
}
void* thread_run2(void *arg)
{
    printf("this is thread%d:pid:%d,tid:%u\n",(int)arg,getpid(),pthread_self());
    pthread_exit((void*)1);
}
void* thread_run3(void *arg)
{
//    sleep(2);
    printf("this is thread%d:pid:%d,tid:%u\n",(int)arg,getpid(),pthread_self());
//   return NULL;
}

int main()
{
    pthread_t tid1,tid2,tid3;
    int err1,err2,err3;
    
    err1=pthread_create(&tid1,NULL,thread_run1,(void*)1);
    err2=pthread_create(&tid2,NULL,thread_run2,(void*)2);
    err3=pthread_create(&tid3,NULL,thread_run3,(void*)3);
    if(err1!=0||err2!=0||err3!=0){
        if(err1==0)
            printf("%s\n",strerror(err1));
        if(err2==0)
            printf("%s\n",strerror(err2));
        if(err3==0)
            printf("%s\n",strerror(err3));
    }

    void *status;

    err1=pthread_join(tid1,&status);
    printf("thread1 return,code:%d\n",(int)status);

    err2=pthread_join(tid2,&status);
    printf("thread2 exit,code:%d\n",(int)status);

    err3=pthread_cancel(tid3);
    if(err3!=0){
        printf("%s\n",strerror(err3));
    }
    err3=pthread_join(tid3,&status);
    printf("thread3 cancel,code:%d\n",(int)status);

    int count=5;
    while(count-- >0){
    printf("this is main thread:pid:%d,tid:%u\n",getpid(),pthread_self());
        sleep(1);
    }   
 
    return 0;
}

运行结果:

函数thread_run3()中加上sleep(2)这条语句时,线程tid3还没有执行完就被别的线程取消,所以退出码是-1

wKiom1cTOkqACZj3AAAoWfa-dnE640.png

运行结果:

函数thread_run3()中没有sleep(2)时,线程tid3执行完才被别的线程取消,所以退出码是40,输出的错误消息是No such process

wKiom1cTPYWSlFdPAAAuBvBG1l4172.png

如果thread_run1()中获得当前线程的id不调用pthread_self(),而是定义一个全局变量来表示可以吗?

test.c

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

pthread_t tid1;//全局变量
void* thread_run1(void *arg)
{
    printf("this is thread%d:pid:%d,tid:%u\n",(int)arg,getpid(),tid1);
    return (void*)0;
}
void* thread_run2(void *arg)
{
    printf("this is thread%d:pid:%d,tid:%u\n",(int)arg,getpid(),pthread_self());
    pthread_exit((void*)1);
 }

int main()
{
    pthread_t tid2;
    int err1,err2;

    err1=pthread_create(&tid1,NULL,thread_run1,(void*)1);
    err2=pthread_create(&tid2,NULL,thread_run2,(void*)2);
    if(err1!=0||err2!=0){
        if(err1==0)
            printf("%s\n",strerror(err1));
        if(err2==0)
            printf("%s\n",strerror(err2));
    }

    void *status;
    
    err1=pthread_join(tid1,&status);
    printf("thread1 return,code:%d\n",(int)status);

    err2=pthread_join(tid2,&status);
    printf("thread2 code:%d\n",(int)status);
    printf("this is main thread:pid:%d,tid:%u\n",getpid(),pthread_self());
    
    return 0;
}

运行结果:

wKiom1cTUtGxdYwLAAASqcTCC-M508.png



线程的分离

在任何时刻线程是可结合的(joinable)或者是分离的(detached)


一个可结合的线程可以被其它线程所回收其资源或杀死(等待回收,取消杀死),在被回收之前其资源是不被释放的,而一个可分离的线程在其终止后其资源被系统自动回收,它不可以被其他线程回收或杀死。


一般情况下,线程终止后其资源还未被系统回收,其终止状态一直保留到其它线程调用pthread_join来获取它的退出状态,但其它线程调用pthread_join来等待该线程退出时,会出于阻塞状态,我们有时候不希望阻塞等待,因此可以把线程设置为detach状态,即把设置该线程为分离状态,这样,当线程结束时会立即回收它所占用的资源



一个被设置为分离状态的进程不能被其它线程调用pthread_join来等待

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

pthread_t tid1;
void* thread_run1(void *arg)
{
    int err=pthread_detach(tid1);
    if(err!=0){
        printf("%s\n",strerror(err));
    }
    else
        printf("this is thread%d:pid:%d,tid:%u\n",(int)arg,getpid(),tid1);
    return (void*)5;
}

int main()
{
    int err1;

    err1=pthread_create(&tid1,NULL,thread_run1,(void*)1);
    if(err1!=0)
        printf("%s\n",strerror(err1));

    void *status;

//  sleep(1);
    err1=pthread_join(tid1,&status);
    if(err1!=0)
        printf("%s\n",strerror(err1));
    else
        printf("thread1 return,code:%d\n",(int)status);

    printf("this is main thread:pid:%d,tid:%u\n",getpid(),pthread_self());

    return 0;
}

 运行结果:

  有sleep(1)这条语句

  线程1被设置为分离状态,运行终止后系统自动回收资源,所以主线程等待失败

wKiom1cTZ53QgIQ7AAAWoenQFJc128.png

运行结果:

 屏蔽sleep(1)

wKiom1cTZ53zQGXUAAAU2dg2x2w784.png

   

一个被等待后的线程已经被回收,不能再调用pthread_detach设置分离状态


代码:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

pthread_t tid1;
void* thread_run1(void *arg)
{
    printf("this is thread%d:pid:%d,tid:%u\n",(int)arg,getpid(),tid1);
}

int main()
{
    int err1;

    err1=pthread_create(&tid1,NULL,thread_run1,(void*)1);
    if(err1!=0)
        printf("%s\n",strerror(err1));

    void *status;

    err1=pthread_join(tid1,&status);
    if(err1!=0)
        printf("%s\n",strerror(err1));
    else
        printf("thread1 return,code:%d\n",(int)status);

    err1=pthread_detach(tid1);
    if(err1!=0)
        printf("detach is failed:%s\n",strerror(err1));


    printf("this is main thread:pid:%d,tid:%u\n",getpid(),pthread_self());

    return 0;
 }

运行结果:

wKioL1cTcgOg49zwAAAYZu84YGo969.png