一、线程和进程的关系
- 进程与线程最大的不同就是线程没有系统资源。
- 线程是操作系统调度的最小单位
- 在一个进程内部多个线程之间的资源是共享的。系统不会为线程分配系统资源
- 工作效率:进程拥有系统资源,在进程切换的时候操作系统需要保留进程占用的资源,因此线程切换效率高于进程。
- 线程的有程序运行的入口函数,但是不能独立运行。由于线程不占有系统资源,所以线程必须放在进程中运行。
- 线程依赖进程存在的,如果创建线程的进程结束了,线程也就结束了
进程可以被操作系统直接调度。在一个进程内部的多个线程可以共享资源和调度,不同进程直接的线程资源是不能共享的。进程可以认为是线程的集合。
二、线程创建
#include "pthread.h"
/**
* thread:句柄(线程ID)
* attr:设置线程属性(后面介绍),如果没有特俗需求,设置未NULL
* start_routine:入口函数
* arg:入口函数的参数
* return:线程创建成功返回0,失败返回错误码
**/
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*), void* restrict arg);
其中restrict是C99标准增加的关键字,作用是限制指针:使用restrict修饰的指针所指向的数据仅能被该指针所用,其他指针无法访问这块数据。
三、脱离线程
创建一个线程默认的状态是joinable(线程属性), 如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收(退出状态码),所以创建线程者应该pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于wait,waitpid)
但是调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此,比如在Web服务器中当主线程为每个新来的链接创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的链接),这时就有了脱离线程的概念。
#include "pthread.h"
pthread_detach(pthread_self());//可以在子线程中加入代码脱离线程
pthread_detach(thread_id);//父线程调用脱离线程(非阻塞,可立即返回)
四、线程取消
#include "pthread.h"
/**
* thread:将要取消的线程ID
* return:线程创建成功返回0,失败返回错误码
**/
int pthread_cancel(pthread_t thread);
五、线程退出
#include "pthread.h"
/**
*retval:线程返回值(该值不能是局部变量,局部变量在线程调用该函数后就不存在了)
**/
void pthread_exit(void* retval);
六、等待线程结束
#include "pthread.h"
/**
* thread:需要等待的线程ID
* value_ptr:指向退出线程的返回值(四中的retval)
* return:线程创建成功返回0,失败返回错误码
**/
int pthread_join(pthread_t thread, void **value_ptr);//阻塞
七、线程属性
#include "pthread.h"
/**
* 初始化一个线程属性对象
* attr:线程属性对象
* return:成功返回0,失败返回错误码
**/
int pthread_attr_init(pthread_attr_t *attr);
/**
* 删除一个线程属性对象,删除之后属性对象不能再使用,只能从新初始化。
* attr:线程属性对象
* return:成功返回0,失败返回错误码
**/
int pthread_attr_destroy(pthread_attr_t *attr);
对象初始化之后,可以调用许多其他函数来设置不同的属性行为。常用的函数如下:
/*
* 设置绑定属性:
* attr:线程属性对象
* return:成功返回0,失败返回错误码
*/
int pthread_attr_setscope(pthread_attr_t *attr, int cope);
/*
* 设置线程分离属性:
* attr:线程属性对象
* detachstate:PTHREAD_CREATE_JOINABLE(默认)
* PTHREAD_CREATE_DETACHED
* return:成功返回0,失败返回错误码
*/
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
/*
* 设置/获取调度策略:
* attr:线程属性对象
* policy:调度方式,可以是:
* SCHED_OTHER(默认):分时调度策略
* SCHED_RP:实时调度策略,时间片轮转。当进程的时间片用完,系统将重新分配时间片,并重新放置时间尾。放置尾尾保证了所有具有相同优先级的RR任务的调度公平
* SCHED_FIFO:实时调度策略,先到先服务。一旦占用cpu则一直运行。一直运行直到有更高优先级任务到达或自己放弃
* return:成功返回0,失败返回错误码
*/
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(pthread_attr_t *attr, int policy);
/*
* 设置/获取SCHED_OTHER调度策略的优先级(详见7.1):和线程调度策略(schedpolicy)结合使用,可以对SCHED_OTHER策略运行的线程的调度(优先级)进行控制。
* attr:线程属性对象
* param:
* return:成功返回0,失败返回错误码
*/
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
/*
* 设置/获取线程堆栈大小:该接口属于POSIX规范中可选部分,只有定义了宏——POSIX_THREAD_ATTR_STACKSIZE的版本才支持
* attr:线程属性对象
* stacksize:线程堆栈大小
* return:成功返回0,失败返回错误码
*/
int pthread_attr_setstacksize(pthread_attr_t * attr,int stacksize);
int pthread_attr_getstacksize(pthread_attr_t * attr,int stacksize);
7.1 设置线程优先级流程(调度)
- 定义优先级需要的变量
- 设置脱离属性
- 设置调度策略
- 查找允许优先级范围
- 设置优先级
//1、定义变量
int max_priority;
int min_priority;
struct sched_param scheduling_value;
pthread_attr_t thread_attr;
//2、设置脱离属性
ret = pthread_attr_init(&thread_attr);
if(ret)
{
perror("thread_attr init failed\n");
}
pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_DETACHED);//判断返回值
//3、设置调度策略
pthread_attr_setschedpolicy(&thread_attr, SCHED_OTHER);//判断返回值
//4、查询允许的优先级范围
max_priority = sched_get_priority_max(SCHED_OTHER);
min_priority = sched_get_priority_min(SCHED_OTHER);
//5、设置优先级
scheduling_value.sched_priority = min_priority;
pthread_attr_setschedparam(&thread_attr, &scheduling_value);//判断返回值
八、实例
创建两个线程,线程1的句柄作为线程2的入参。在主进程中等待线程1和线程2的结束,在线程2中删除线程1之后退出。
#include "pthread.h"
#include "stdlib.h"
#include "stdio.h"
#include "unistd.h"
void *thread_1_fun(void *arg);
void *thread_2_fun(void *arg);
int main()
{
pthread_t thread_1,thread_2;
int res;
res = pthread_create(&thread_1,NULL,thread_1_fun,NULL);
if(!res)
{
printf("thread create thread1 faild:%d\n",res);
}
res = pthread_create(&thread_2,NULL,thread_2_fun,&thread_1);
if(!res)
{
printf("thread create thread2 faild:%d\n",res);
}
printf("goto wait thread1\n");
if(pthread_join(thread_1,NULL))
{
printf("wait thread1 faild\n");
}
printf("goto wait thread2\n");
if(pthread_join(thread_2,NULL))
{
printf("wait thread2 faild\n");
}
return 0;
}
void* thread_1_fun(void *arg)
{
int times =0;
printf("thread_1 create \n");
while(1)
{
printf("thread_1 run times:%d \n",times);
times++;
sleep(1);
}
}
void* thread_2_fun(void *arg)
{
pthread_t* thread;
printf("thread_2 create \n");
thread = arg;
sleep(5);
if(arg != NULL)
{
pthread_cancel(*thread);
printf("cancel thread_1 \n");
}
}