进程与线程

目录

1 基本概念

2 进程线程对比

3 线程共享资源

4 线程不共享资源

5 线程使用

pthread_create创建线程库函数

pthread_self查看线程ID

pthread_exit线程退出

pthread_join 线程回收

pthread_cancel 杀死线程

pthread_testcancel 取消点的概念

线程分离状态

概念

pthread_detach 线程分离

pthread_create 创建同时设置线程分离

pthread_equal 比较线程id是否相等

6 线程使用注意事项


1 基本概念

进程是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位。

2 进程线程对比

进程:有PCB,有独立地址空间,进程间是相互独立的,只有通过各种进程间通信手段才能进行通信。

线程:也有PCB(因为在linux内核角度来看线程就是进程),但是没有独立地址空间,与进程是共享空间的,或者说多个线程共享空间。

线程LWP:light weight process,即线程就是轻量级的进程,在linux环境下线程的本质还是进程。因此创建线程用的底层函数和进程一样,都是clone(fork与pthread_create底层都是clone)。因此在内核层面,当我们创建一个进程,如果复制了当前进程的地址空间,那么我们就相当于创建了一个子进程;如果共享当前进程的地址空间,那么就是创建了一个线程。即Linux内核是不区分进程与线程的,只在用户层面区分。因为线程只是用户层面的概念,因此其所有操作函数pthread*都是库函数,而不是系统调用。

一个进程创建多少个线程,他们都共用一块地址空间,但是线程越多,占用cpu越多,也就是cpu分的时间片越多,效率越高。因为线程是最小的执行单位,要被执行,必须要用cpu。

3 线程共享资源

文件描述符表

每种信号的处理方法(默认或自定义处理方式)

当前工作目录

用户ID和组ID

虚拟地址空间中的 text/data/bss/heap/共享库 等部分,不共享栈

4 线程不共享资源

线程ID

处理器线程和栈指针(内核栈)

独立的栈空间(用户栈)

errno变量

信号屏蔽字(信号用于进程间通信,最好不要多线程与信号同时使用)

调度优先级(每个线程的调度优先级不同)

 

5 线程使用

pthread_create创建线程库函数

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

pthread 线程id,是传出参数,需要先定义一个pthread_t类型的变量接收

attr线程的属性,一般不用

void* (*start_routine)(void *) 函数指针,指向线程要执行的函数,该函数返回void*类型参数也是void*类型

arg 线程执行函数的参数

返回值:成功返回0,失败返回errno

编译和链接的时候都要加上pthread库

pthread_self查看线程ID

#include<pthread.h>

pthread_t pthread_self(void)

例子:

pthread_exit线程退出

#include <pthread.h>

void pthread_exit(void *retval);

retval是记录退出值得,是传入参数不是传出参数,相当于return后边的东西,暂时不用管

上一版代码中为了避免进程退出导致所有线程结束而采取了sleep的方法,其实可以用pthread_exit函数在main函数中退出主控线程而不退出整个进程。

需要注意的是,在我们写的线程执行函数里可以用return或者pthread_exit来退出线程都是等价的,但是在主控线程(main)中使用return会导致整个进程退出。exit永远是退出整个进程。即:

pthread_exit:退出线程,不退出进程。

return:在主控线程中代表退出整个进程,在线程中只退出线程。

exit:任何情况下都是退出整个进程。

pthread_join 线程回收

#include <pthread.h>

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

thread:线程id,即pthread_create的第一个参数。

retval:接收线程返回值,是一个传出参数。因为线程执行函数是void*类型的,传出参数的类型是线程执行函数返回值类型的指针,因此是void**类型的二级指针,用来接收线程执行函数的返回值。

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

// 线程执行函数
void *thr(void *arg){
    printf("I am a thread, pid = %d, tie = %lu", getpid(), pthread_self());
    pthread_exit((void *)10086);

}

int main(){
    pthread_t tid;
    pthread_create(&tid, NULL, thr, NULL);
    printf("I am a main thread, pid = %d, tid = %lu\n", getpid(), pthread_self());
    void *ret;
    pthread_join(tid, &ret);
    printf("thr exit with %d\n", (int)ret);
    return 0;

}

pthread_join默认是阻塞等待的,如果线程一直没返回,主控线程会卡在这里。

pthread_cancel 杀死线程

int pthread_cancel(pthread_t thread);
    thread:线程id
    返回值:失败返回errno,成功返回0

被pthread_cancel杀死的线程,退出状态为PTHREAD_CANCELED,这个宏的值其实就是把 -1 强转成了void* 类型,即 #define PTHREAD_CANCELED((void *)-1)

因此把一个线程杀死以后,如果再回收的话,回收到的结果是-1

pthread_testcancel 取消点的概念

#include <pthread.h>

void pthread_testcancel(void);

如果线程执行函数中没有具体的语句,那么就没有取消点。

如果没有取消点,就没办法用pthread_cancel杀死线程。

所以需要至少写一条语句,或者用pthread_testcancel函数直接设置取消点。

线程分离状态

概念

线程的分离状态决定了一个线程以什么样的方式来终止,线程的默认属性是非分离状态。

非分离状态:主控线程中调用pthread_join阻塞等待创建的线程结束,只有当主控线程中pthread_join函数返回时,创建的线程才算真正终止,才能释放自己所占的所有资源。

分离状态:主控线程不会阻塞等待分离状态的线程,线程自己运行结束就终止,马上释放所有系统资源。

pthread_detach 线程分离

#include <pthread.h>

int pthread_detach(pthread_t thread);

线程分离的目的是使得当前线程与主控线程失去联系,从而实现该线程自动回收资源的目的,这样就不用pthread_join来主动回收资源了。

用pthread_detach来设置线程分离的话,只需要在主控线程中调用该函数并传入需要设置分离的线程id即可。

pthread_create 创建同时设置线程分离

在pthread_create函数中,第二个参数attr我们上边没有使用,这个参数是用来设置线程的属性的,包括线程是否是分离状态。

主要有五个步骤:

第一步:定义属性结构体对象 

pthread_attr_t attr;

第二步:初始化属性结构体对象

pthread_attr_init(&attr);

第三步:给属性结构体对象加上线程分离属性

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

第四步:pthread_create创建线程同时设置线程分离

pthread_create(&tid, &attr, thr, NULL);

第五步:摧毁属性结构体对象

pthread_attr_destroy(&attr);

pthread_equal 比较线程id是否相等

线程id的类型是pthread_t其实就是个长整型数,直接用 == 就能判断,这里是未雨绸缪,怕的是将来pthread_t会用一个结构体来实现。

int pthread_equal(pthread_t t1, pthread_t t2);

 

6 线程使用注意事项

创建线程数经验公式:cpu核数*2 +2,如果是做科学计算,那么直接乘以2。

如果让主线程退出其它线程不退出,主线程应调用pthread_exit,不能用return。

避免僵尸线程有三种方法:pthread_join回收;pthread_detach设置线程分离,pthread_cerate创建线程同时指定分离状态。

malloc和mmap申请的内存可以被其他线程释放。

 

 

 

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值