linux下的线程

一、linux下的进程与线程
进程:
进程是指在系统中能够独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成,并且每一个进程都有一个进程控制块PCB,在Linux下被称为task_struct。另外每个进程还有一个独立的地址,它和物理内存通过页表联系在一起。
线程:
线程是进程的一个部分,它在进程的地址空间上运行,和主线程一起共享虚拟地址空间的资源,一个没有线程的进程可以看做是单线程,线程主要是作为系统调度和分派的基本单位,Linux下没有线程的概念,系统将线程看做轻量级进程,这里的轻量级是因为线程没有自己独立的地址空间和相关数据结构。
二、进程与线程的区别
区别:
1、进程是拥有系统资源的一个独立单位,而线程不能拥有资源,但它可以访问创建它的进程中的资源。
2、任一时刻,cpu只能运行一个进程,其他进程则处于非运行状态,而任一时刻,线程可以同时执行,还可以协同完成一项任务,这是线程的并发性。
3、当我们创建一个进程时,系统通过写时拷贝的方式给被创建的进程分配了地址空间和相关数据结构,而当我们创建一个线程时,这个线程会和当前进程中的其他线程一起共享地址空间中的资源。
下面是自我总结的:

1、进程是作为程序的一次执行(独立的内存空间)
2、线程是CPU的基本调度单位
3、线程间的同步是为了防竞争(因同时修改,导致数据不一致的问题)
4、进程的内存空间是天然独立的,线程的内存空间是天然共享的
5、线程在执行期间需要协作同步,不同进程的线程需要利用消息通信来实现同步
6、线程在执行的时候与进程还是有区别的,每个独立执行的线程都有一个程序执行的入口,顺序执行序列和程序的出口。但是线程不能独立执行,由应用程序来实现多个线程的执行控制。
7、从逻辑上看,多线程是在应用程序中有多个执行部分可以同时执行,但操作系统并没有将多个线程看成是多个应用。

三、线程的属性及线程的创建
线程的属性:
在linux下是没有真正意义上的线程,linux是通过进程来模拟实现线程的。这些模拟出来的线程又被叫做是轻量级线程。我们知道每一个进程都有一个pcb,里面存放的是进程所有的数据与信息,操作系统用过控制进程PCB来控制进程,而进程又是通过PCB、地址空间、页表来访问物理内存的。
如下图:
这里写图片描述
当创建多个线程的时候,就会有多个PCB这些PCB共享地址空间,也就是这些线程共享同一份进程资源和环境。
这里写图片描述
线程共享的资源包括:
1、文件描述符表
2、每种信号的处理方式(SIG_IGN、SIG_DFL或自定义的信号处理函数)
3、当前工作目录
4、用户id和组id

线程独占的资源:
1、线程id
2、上下文(包括各种寄存器的值、程序计数器、栈指针)
3、栈空间
4、errno变量
5、信号屏蔽字
6、调度优先级

线程相关的函数:
1、线程的创建:
int pthread_create(pthread_t tid,const pthread_attr_t *attr,void (start_routine)(void),void *arg)
返回值:成功返回0,失败返回错误码。可以通过char *strerror(int errnum)函数提取错误信息。
tid:用来保存线程的id。属于一个输出型参数,将线程的id保存起来。
attr:用来修改线程的属性。如果为NULL的时候表示创建的线程是默认属性
start_routine:start_routine是一个函数指针,它指向一个返回值为void*,参数为void* 函数。该线程创建好了以后就会执行这个函数。当start_routine返回之后该线程就退出了。
arg:是start_routine所指向函数的参数。
代码展示:

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

//新线程,每秒打印一次,共打印五次。
void* thread_run(void *arg)
{
    int count = 0;
    while(count++ < 5)
    {
        sleep(1);
        printf("thread , pid: %d , ppid: %d , tid: %lu\n",getpid(),getppid(),pthread_self());
    }
    printf("thread is over...\n");
    return  NULL;
}


int main()
{
    //主线程
    printf("phread\n");
    pthread_t tid;
    int ret = pthread_create(&tid,NULL,thread_run,pthread_self());
    if(ret != 0)
    {
        printf("pthread_creat error\n");
        return -1;
    }
    else
    {
            sleep(1);
            printf("main , pid: %d , ppid: %d , tid: %lu\n",getpid(),getppid(),pthread_self());
    }

    int exitCode;
    pthread_join(tid,(void**)&exitCode);//用来等待新线程的结束

    printf("main is over...%d\n",exitCode);
    return 0;
}

执行结果:
这里写图片描述
pthread_join函数,此函数的功能就是等待一个线程的结束。此例中让主线程等待新线程的结束。第一个参数为新线程的线程ID,第二个参数用来接收新线程的退出码。
执行结果应为主线程打印1次,新线程打印五次:
四、线程终止
线程的终止有三种方式:
1、return
return可以终止一个线程,在main函数中return就相当于是一个进程的退出,因为main函数的线程是主线程,主线程返回的话就相当于是一个进程的推出,那么所有的线程都会退出。(相当于是调用exit函数或者_exit函数)
2、void pthread_exit(void* retval)
这个函数就是线程退出的函数,参数是用来传退出的信息的,传给被等待它的线程。
3、int pthread_cancel(pthread_ tid)
这个是线程取消的函数也可以用来终止线程。
在一个线程中调用pthread_cancel函数可以终止另一个线程。假设A线程是被pthread_cancel异常终止的,则pthread_join获取的线程A的退出码就是PTHREAD_CANCEL,这个宏的值是-1,可在pthread.h头文件中找到。
返回值:成功返回0,失败返回错误码。
tid:要终止的线程的id。
代码展示:

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

void* thread_run(void *arg)
{
    int count = 0;
    while(count++ < 5)
    {
        sleep(1);
        printf("thread , pid: %d , ppid: %d , tid: %lu\n",getpid(),getppid(),pthread_self());
    }
    printf("thread is over...\n");
    return  NULL;
}
int main()
{
    printf("phread\n");
    pthread_t tid;
    int ret = pthread_create(&tid,NULL,thread_run,pthread_self());
    if(ret != 0)
    {
        printf("pthread_creat error\n");
        return -1;
    }
    else
    {
            sleep(1);
            printf("main , pid: %d , ppid: %d , tid: %lu\n",getpid(),getppid(),pthread_self());
    }
    pthread_cancel(tid);//在主线程中结束子线程

    int exitCode;
    pthread_join(tid,(void**)&exitCode);

    printf("main is over...%d\n",exitCode);
    return 0;
}

主线程打印一次然后直接退出,我们可以看出线程的这里写图片描述]退出码是-1。一个线程被其他线程调用pthread_cancel异常终止掉,它返回的退出码将是常数PTHREAD_CANCELED。这个宏被定义在pthread.h中,值为-1。
这里写图片描述
那么如果一个线程自己终止自己又是什么情况呢?
代码展示:

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

void* thread_run(void *arg)
{
    int count = 0;
    pthread_t mid = *(pthread_t*)arg;
    while(count++ < 5)
    {
        sleep(1);
        printf("thread , pid: %d , ppid: %d , tid: %lu\n",getpid(),getppid(),pthread_self());
    }
    printf("thread is over...\n");
    pthread_exit((void*)10);
}
int main()
{
    printf("phread\n");
    pthread_t tid;
    int ret = pthread_create(&tid,NULL,thread_run,pthread_self());
    if(ret != 0)
    {
        printf("pthread_creat error\n");
        return -1;
    }
    else
    {
            sleep(1);
            printf("main , pid: %d , ppid: %d , tid: %lu\n",getpid(),getppid(),pthread_self());
    }
    int exitCode;
    pthread_join(tid,(void**)&exitCode);
    printf("main is over...%d\n",exitCode);
    return 0;
}

运行结果:
这里写图片描述
这个线程的退出码是10
五、线程的可结合与可分离
在讲这个之前还是先来看看什么是线程的可结合与可分离吧。

在任何一个时间点上,线程是可结合或可分离的。

可结合(joinable):
一个可结合的线程能够被其他线程回收资源和杀死,在被回收之前它的存储器资源是不释放的。
可分离(detached):
一个可分离的线程不能被其他线程回收资源和杀死,它的存储器资源在它终止的时候由系统自动释放。一个可分离的线程是不能被等待的。
在默认的情况下,一个线程在创建的时候会被创建成一个可结合的线程,为避免内存的泄漏每个可结合的线程都要被系统显示回收资源进行释放。(pthread_join,没有被join会造成资源、内存的泄漏)
调用pthread_join后,如果应该被join的新线程没有运行结束,调用者会被阻塞,这时可在新线程中加入代码pthread_detach(pthread_self())或者主线程调用pthread_detach(thread_id)来将新线程设置成可分离的,如此一来,新线程运行结束后会自动释放所有资源。
六、线程的等待
线程等待的函数:int pthread_join(pthread_t tid,void** retval)
这个函数的功能是进行线程的等待,等待线程的退出,回收线程的退出信息并释放资源。
实际的情况:
在主线程中创建一个新的线程, 当主线程要退出的时候新的线程还在运行着,如果这个时候退出主线程的话就会导致错误,所以主线程就得等待新线程跑完才能退出,这也即是线程的等待的目的。
返回值:成功的话就返回0,失败就返回的是错误码。
参数信息:
tid:要等待的线程的id
retval:获取线程的退出信息,一般情况线程退出是不会马上释放所占的资源信息,而是要等有人来获取它的退出信息,否则会造成内存泄漏。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值