一、多线程
1. 线程和进程的联系和区别
联系:
第一个:线程从进程资源中分割一部分创建出来的
第二个:线程和进程都是能实现多任务的并发执行,实际开发中还是用线程会多一点
区别:
第一个:线程创建的时候不需要消耗额外的系统资源
进程创建的时候需要消耗额外的系统资源
线程的特点:
创建了多个线程,多个线程执行顺序是随机的
理论概念:
主线程和子线程: 调用creat函数创建子线程的进程称为主线程,被创建的线程称为子线程
孤儿线程: 主线程创建的子线程,如果主线程不回收,子线程就变成了孤儿线程
2. 线程有关的接口函数
(一)创建线程
#include<pthread.h>
int pthread_create( pthread_t *id, const pthread_attr_t *attr , void*(*start_rtn)(void*), void *arg );
参数:id --- 存放线程的id
attr --- 重点,线程的属性,一般设置为NULL,表示使用系统默认的属性
void* (*start_rtn)(void*) --- 重点,函数指针,线程的任务函数,线程需要完成的任务就靠这个函数指针指向的函数去实现
arg --- 传递给start_rtn的参数(线程跟主函数之间传递参数)
注意:只要你的代码中使用了跟线程有关的函数,编译的时候必须链接线程库
gcc hello.c -o hello -pthread
gcc hello.c -o hello -lpthread 都正确
(二)线程的退出和回收
(1)线程的退出
#include<pthread.h>
void pthread_exit(void *retval);
参数:retval --- 存放线程退出时候的信息
(2)线程的回收(阻塞主函数,等待子线程的退出)
#include<pthread.h>
int pthread_join(pthread_t thread, void **retval);
参数:thread --- 你要回收的那个线程的id号
retval --- 用来存放是 pthread_exit函数中参数的地址(存放线程退出时的信息)
(三)线程的取消(强行干掉线程)
#include<pthread.h>
int pthread_cancel(pthread_t thread);
参数:thread --- 线程id
(1)设置线程的取消状态
#include<pthread.h>
int pthread_setcancelstate(int state, int *oldstate); //设置线程取消状态
线程的取消状态有两种: 可以取消和不可以取消
参数:state --- PTHREAD_CANCEL_ENABLE //能取消,线程默认就是能被取消
PTHREAD_CANCEL_DISABLE //不能取消
oldstate --- 存放线程原本的取消状态
(2)设置线程的取消类型
#include<pthread.h>
int pthread_setcanceltype(int type, int *oldtype); //设置线程取消类型(了解)
线程的取消类型有两种:
立即取消: 只要你调用了pthread_cancel()函数,线程就立马退出
延时取消: 只要你调用了pthread_cancel()函数,线程不会立即退出,会继续执行直到遇到下一个取消点才退出
取消点: linux系统中定义的一系列函数,这些函数就是线程的取消点函数 man 7 pthreads查看到系统中所有的取消点函数
参数:type --- PTHREAD_CANCEL_DEFERRED //延时取消
PTHREAD_CANCEL_ASYNCHRONOUS //立即取消
oldtype --- 存放线程原本的取消类型
(四)线程的属性
(1) 线程属性结构如下:
typedef struct
{
int detachstate; 线程的分离状态
int schedpolicy; 线程调度策略
struct sched_param schedparam; 线程的调度参数
int inheritsched; 线程的继承性
int scope; 线程的作用域
size_t guardsize; 线程栈末尾的警戒缓冲区大小
int stackaddr_set;
void * stackaddr; 线程栈的位置
size_t stacksize; 线程栈的大小
}pthread_attr_t;
规律: linux中已经把这个结构体中所有的属性设置获取都封装成了函数
设置属性: pthread_attr_setxxxxx
获取属性: pthread_attr_getxxxxx
比如:分离属性分为两种,一种可分离,另一种不可分离
可分离: 线程退出之后不需要主函数去回收,线程自己回收自己,线程可以自生自灭(主函数想回收也回收不了并且pthread_join函数也不会阻塞了)
不可分离: 线程退出之后,需要主函数去回收(线程默认就是不可分离)
(2)设置线程属性的套路
①定义线程属性变量并初始化
#include <pthread.h>
pthread_attr_t myattr;//定义一个线程属性结构体
pthread_attr_init(&myattr); //初始化线程的属性
参数:myattr --- 把定义的线程属性结构体作为地址传入
②调用对应的属性设置函数去设置属性
比如:设置分离属性
#include <pthread.h>
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
参数:detachstate --- PTHREAD_CREATE_DETACHED 可以分离
PTHREAD_CREATE_JOINABLE 不可分离
③调用线程的创建函数,并把属性作为参数传递过来
#include <pthread.h>
pthread_create(&id,&myattr,);//myattr对应线程创建函数中的参数attr
④销毁线程的属性
#include <pthread.h>
int pthread_attr_destroy(pthread_attr_t *attr);