这个概念不管在什么操作系统中都是一样的,也是面试官比较喜欢问的,代表你对程序优化的功底,搞安卓的时候,经常被用来优化处理速度 还有数据的处理,结合handler 一起处理,解决经常出现界面死掉问题。
既然总结了,这一次充分总结下:
进程组成:
进程控制块PCB 是内核中存放的一块PCB区域,记录型数据结构 ,PCB 记录了操作系统所需要的参数,用来描述进程目前情况和进程运行的全部信息,包括
进程的状态: 就绪、执行、阻塞
上面是进程的各种状态切换
当第一初始化进程的时候,进程进入就绪状态,等待OS 分配处理机处理进程,当进程获取处理机处理,进程执行阶段,执行的时候,一般会用来处理各种事物,比如 请求 IO 处理等,但是一旦有大量IO 处理,容易进入阻塞状态,此时就是我们经常看见电脑卡死的状态,但是处理完后,进程会进入就绪状态
其实还有一种状态,finish 结束状态,但是只要进程死了就直接退出了
从上面很容易看出来,创建一个进程,因为直接和linux 内核进行处理数据,需要提供大量的参数,来保障进程的安全,高效,稳定,消耗很多系统资源,但是线程就不一样,可以原理是一样的,作为轻量级,消耗系统资源很少,而且作为开发者来说,线程用起来更爽,更方便,线程只能跑在进程中,所以,和系统内核交互的数据都交给进程了,线程简单方便易用,据说进程消耗资源是线程的30倍…..
线程和处理流程和 进程差不错 ,也是 就绪、执行、阻塞、结束四个状态
多线程编程的API
创建线程
int pthread_create(pthread_t *thread,pthread_attr_t *attr,void * (*func)(void * ),void *arg);
/*
看了上面的方法,mmp、wtf 这些词汇从我脑海中冒出来,函数参数要不要写的那么复杂
只能看调用示例了:
pthread(&thrd1,NULL,(void*)task1,(void*)&g1);
phtread thrd1 表示创建线程的标识
pthread_attr_t *attr 表示线程属性NULL 默认
void task1 线程需要执行的代码
int g1 表示task1的参数
*/
结束线程
pthread_exit(void *retval);
//retval 用来存放自杀线程退出状态
等待线程结束
// 多个线程启动后,由系统负责调度,但我们并不知道哪个会先开始和结束,只能等待
int pthread_join(pthread_t th,void **thread_return);
/*
th 等待线程的标识
thread_return 返回的状态
*/
多线程实例:
只有同步锁的情况,线程之间的同步互斥
用同步互斥原理,实现对变量shareid的操作
int shareid=0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int main(int argc, char const *argv[]) {
pthread_t thrd1,thrd2;
int ret;
ret = pthread_create(&thrd1,NULL,(void *)task1,NULL);//创建线程
ret = pthread_create(&thrd2,NULL,(void *)task2,NULL);
pthread_join(thrd1,NULL);//等待线程结束
pthread_join(thrd2,NULL);
printf("shareid = %d\n",shareid );
return 0;
}
void task1(void) {
long i,tmp;
for ( i = 0; i < 10000; i++) {
if (pthread_mutex_lock(&mutex)!=0) {//加同步锁
perror("pthread_mutex_lock");
exit(EXIT_FAILURE);
}
tmp = shareid;
tmp = tmp+1;
shareid = tmp;
if (pthread_mutex_unlock(&mutex)!=0) {//解锁
perror("pthread_mutex_unlock");
exit(EXIT_FAILURE);
}
}
printf("task1 finished shareid = %d\n" ,shareid);
}
void task2(void) {
long i,tmp;
for ( i = 0; i < 5000; i++) {
if (pthread_mutex_lock(&mutex)!=0) {
perror("pthread_mutex_lock");
exit(EXIT_FAILURE);
}
tmp = shareid;
tmp = tmp+1;
shareid = tmp;
if (pthread_mutex_unlock(&mutex)!=0) {
perror("pthread_mutex_unlock");
exit(EXIT_FAILURE);
}
}
printf("task2 finished shareid = %d\n" ,shareid);
}
因为 pthread 不是gcc 编译标准库,需要用到动态库加载,编译编译命令最后 加上-lpthread
gcc pthread_create.c -o pthread_create -lpthread
debug 结果:
线程+同步锁+信号量 会有更有意思
很明显同步锁,有点死板,每次只能操作一个变量,不能实现多个公共资源的操作,但是加上信号量就有意思了
semaphore API 介绍
创建信号量
#include
int sem_init(sem_t *sem,int pshared,unsigned int value);
/*
功能: 初始化 信号量
返回值: 成功返回0 错误返回-1
sem: 指向信号量结构的指针
pshared 不为0 信号量在进程间共享,不然只能在本进程中的所有线程中共享
value 给出信号量的初始值
*/
信号量 P操作 -1
int sem_wait(sem_t *sem);
信号量 V操作 +1
int sem_post(sem_t *sem);
信号量删除
int sem_destroy(sem_t *sem);
看了上面的API,和进程中用的API 不一样? linux ipc 进程间通信总结
我只能这么理解了,进程和线程适用不同的信号量API
案例如下:
# define MAXSIZE 10
int stack[MAXSIZE][2];
int size = 0;
sem_t sem;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void provide_data(void){
int i;
for (i = 0; i < MAXSIZE; i++) {
stack[i][0] = i;
stack[i][1] = i;
sem_post(&sem);// 信号量 V操作 +1
}
}
void handle_data1(void) {
int i;
while (pthread_mutex_lock(&mutex),((i =size++)printf("Plus : %d + %d =%d \n",stack[i][0],stack[i][1],stack[i][0]+stack[i][1] );
}
pthread_mutex_unlock(&mutex);
}
void handle_data2(void) {
int i;
while (pthread_mutex_lock(&mutex),((i =size++)printf("Multiple : %d × %d =%d \n",stack[i][0],stack[i][1],stack[i][0]*stack[i][1] );
}
pthread_mutex_unlock(&mutex);
}
int main(int argc, char const *argv[]) {
pthread_t thd1,thd2,thd3;
sem_init(&sem,0,0);
pthread_create(&thd1,NULL,(void *)handle_data1,NULL);
pthread_create(&thd2,NULL,(void *)handle_data2,NULL);
pthread_create(&thd3,NULL,(void *)provide_data,NULL);
pthread_join(thd1,NULL);
pthread_join(thd2,NULL);
pthread_join(thd3,NULL);
sem_destroy(&sem);
return 0;
}
有一个线程th3对 stack 循环赋值,并且每赋值一次 将信号量sem V操作,thd1和thd2这两个线程会竞争获取stack 操作 thd1 是求和,而thd2是求商,哪一个线程获取资源了,求和或者求商,然后对sem进程P操作,所以每次运行程序会打印不同的结果
这里还有一个知识点, 逗号运算符
while (pthread_mutex_lock(&mutex),((i =size++)/*
while 有两个表达式,最后只判断((i =size++)
逗号表达式: 逗号前面和后面的表达式都执行,但是只有最后一个返回值有效
pthread_create 上面第二个参数一直设置NULL 系统默认属性,但是如果我们设置,该如何设置呢?
如果设置线程属性,必须在pthread_create 之前使用pthread_attr_init.
pthread_attr_t 结构体包含 是否绑定,是否分离,堆栈地址,堆栈大小,优先级信息
默认属性是 非绑定 非分离 默认1M大小堆栈 优先级和进程一样
轻进程概念,理解为内核进程,位于用户层和系统层之间,系统对线程资源的分配通过轻进程实现,如果设置绑定在轻进程,那个线程响应度高。
绑定状态
设置绑定状态的函数 pthread_attr_setscope
绑定:PTHREAD_SCOPE_SYSTEM 非绑定 PTHREAD_SCOPE_PROCESS
分离状态
线程的分离状态,用来决定如何结束自己
非分离状态使用pthread_join 处理,才能释放资源,分离状态的线程,运行结束,自动释放
非分离:PTHREAD_CREATE_JOINABLE 分离:PTHREAD_CREATE_DETACHED 设置参数的函数 pthread_attr_setdetachstate如果设置线程分离状态,线程运行太快,在pthread_attr_init 之后,但是在pthread_create之前结束,那么此时创建线程,会得到错误线程号,需要避免,最简单的方法 执行pthread_cond_timewait 使得线程慢点跑
线程优先级
线程优先级 存放在结构体sched_param 用pthread_attr_getsparam 函数 和pthread_attr_setschedpara 存放, 先取得优先级,修改后,存放回去
线程创建后改变属性:
杀死其他线程的方法 pthread_cancel(thread)
但是线程可以设置属性 拒绝被杀死
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL)
线程真的很不错的,优化用的特别多