嵌入式学习 D29:系统编程--线程
- 线程的基本概念
- 线程实现原理
- 线程相关函数
- 线程与进程的区别
一、线程的基本概念
Linux中,线程又叫做轻量级进程(light-weight process LWP),也有PCB,创建线程使用的底层函数和进程底层一样,都是clone,但没有独立的地址空间;而进程有独立地址空间,拥有PCB。
Linux下:线程是最小的执行单位,调度的基本单位。进程是最小分配资源单位,可看成是只有一个线程的进程。
特征:
1)共享资源;
2)效率高;
3)三方库:pthread clone posix
编写代码头文件:pthread.h
编译代码加载库:-lpthread gcc 1.c -lpthread
二、多线程程序设计步骤
1.创建多线程(只要创建成功就启动了
2.线程空间操作(设计函数表达要实现什么功能
3.线程资源回收(栈区回收,默认进程结束栈区不释放
注:
(1)进程中的第一个线程为主线程,主线程有且仅有一个,(主线程号和进程号一
致)其它次线程为平级关系;
(2)主线程不需创建,运行a.out就出来了,后调用函数创建其他空间,在进程空间新
开栈区(8M)。
三、线程相关函数
1. pthread_create函数:创建指定的一个线程(功能)
int pthread_create (pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
参数:
thread 线程id,需要实现定义并由该函数返回。
attr 线程属性,一般是NULL,表示默认属性。
start_routine 指向指针函数的函数指针, 本质上是一个函数的名称即可。称为th 回调函数,是线程的执行空间。
返回值:成功返回0;失败返回相应的错误码。
注意:
-
一次pthread_create执行只能创建一个线程。
-
每个进程至少有一个线程称为主线程。
-
主线程退出则所有创建的子线程都退出。
-
主线程必须有子线程同时运行才算多线程程序。
-
线程id是线程的唯一标识,是CPU维护的一组数字。
-
pstree 查看系统中多线程的对应关系。
-
多个子线程可以执行同一回调函数。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void *th1 (void *arg)
{while(1){printf("发送视频\n");sleep(1);}
}
void *th2 (void *arg)
{while(1){printf("接收控制\n");sleep(1);}
}
int main(int argc, char **argv)
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);while(1)sleep(1);//system("pause");return 0;
}
2.pthread_exit函数:获取当前线程的线程id(谁调就返回谁的id号)
pthread_t pthread_self(void);
返回值:成功即返回当前线程的线程id; 失败 -1。
用户层表示(ps命令)与内核层表示(数字大)不同,使用内核层表示用户层查看(man ps ps-eLf)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
void *th1 (void *arg)
{while(1){printf("发送视频 th1, tid:%ld\n",pthread_self());sleep(1);}
}
void *th2 (void *arg)
{while(1){printf("接收控制th2, tid:%ld\n",pthread_self());sleep(1);}
}int main(int argc, char **argv)
{pthread_t tid1,tid2;pthread_create(&tid1,NULL,th1,NULL);pthread_create(&tid2,NULL,th2,NULL);while(1){printf("main th, tid:%ld\n",pthread_self());sleep(1);}return 0;
}
3. pthread_exit函数:自行退出 = =自杀 ==子线程自己退出
void pthread_exit(void *retval); exit return p;
参数: retval 线程退出时候的返回状态,临死遗言。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void *th(void *arg)
{int i = 3;while (i--){printf("th, tid:%ld\n",pthread_self());sleep(1);}pthread_exit(NULL);
}int main(int argc, char **argv)
{pthread_t tid;pthread_create(&tid,NULL,th,NULL);while(1)sleep(1);system("pause");return 0;
}
4.pthread_cancel函数:强制退出 ==他杀 ==主线程结束子线程
int pthread_cancel(pthread_t thread);
参数:thread 请求结束一个线程id
返回值:成功返0,失败返-1。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void *th(void *arg)
{int i = 3;while (i){printf("th, tid:%ld\n",pthread_self());sleep(1);}pthread_exit(NULL);
}int main(int argc, char **argv)
{pthread_t tid;pthread_create(&tid,NULL,th,NULL);int i = 0;while(1){printf("main th, tid:%ld\n",pthread_self());sleep(1);i ++;if( 3 == i){pthread_cancel(tid);}}system("pause");return 0;
}
线程的回收机制
1、不同与进程没有孤儿线程和僵尸线程;
2、主线程结束任意生成的子线程都会结束;
3、子线程的结束不会影响主线程的运行。
5.pthread_join函数:线程回收
通过该函数可以将指定的线程资源回收(线程结束栈区默认没被释放),该函数具有 阻塞等待功能,如果指定的线程没有结束,则回收线程会阻塞。
int pthread_join(pthread_t thread, void **retval);
参数:thread为要回收的子线程id;retval为要回收的子线程返回值状态;
注:二级指针需要改变指针的指向
返回值:成功返0;失败返-1。
扩展:Tcb块(线程控制块),进程中有10个线程,pcb块中就有10个tcb块。
次线程返回的不能为局部变量(解决办法:加static或malloc申请(+free()))
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void *th(void *arg)
{int i = 3;while (i--){printf("th, tid:%ld\n",pthread_self());sleep(1);}return NULL;
}int main(int argc, char **argv)
{pthread_t tid;pthread_create(&tid,NULL,th,NULL);pthread_join(tid,NULL);system("pause");return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>void *th(void *arg)
{// static char str[] = "我要消亡了";char *p = malloc(20);strcpy(p,"我要消亡了");sleep(3);// return str;return p;
}int main(int argc, char **argv)
{pthread_t tid;pthread_create(&tid,NULL,th,NULL);void *ret;pthread_join(tid,&ret);printf("ret %s\n",(char*)ret);//void* ->qitaleixingzhizhen qiangzhuangfree(ret);system("pause");return 0;
}
次线程的回收策略:
1、如果预估子线程可以有限范围内结束则正常用pthread_join等待回收;
2、如果预估子线程可能休眠或者阻塞则等待一定时间后强制回收;
3、如果子线程已知必须长时间运行则,不再回收其资源。
6.线程的传参:降低程序耦合性(传参>定义全局变量)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>typedef struct
{char name[50];int age;char addr[50];
}PER;void *th(void *arg)
{PER *per = (PER*)arg;printf("th:%s %d %s\n",per->name,per->age,per->addr);strcat(per->name,"1");return per;
}int main(int argc, char **argv)
{PER per;bzero(&per,sizeof(per));printf("pls name:");fgets(per.name,sizeof(per.name),stdin);//zhang\nper.name[strlen(per.name) - 1]= '\0';char buf[5] = {0};printf("pls age:");fgets(buf,sizeof(buf),stdin);per.age = atoi(buf);printf("pls addr:");fgets(per.addr,sizeof(per.addr),stdin);per.addr[strlen(per.addr) - 1]= '\0';pthread_t tid;pthread_create(&tid,NULL,th,&per);//ren yi lei xing dao void* bu yong qiang zhuanvoid *ret = NULL;pthread_join(tid,&ret);printf("join ret:%s %d %s\n",((PER*)ret)->name,((PER*)ret)->age,((PER*)ret)->addr);// pthread_join(tid,NULL);// printf("join ret:%s %d %s\n",per.name,per.age,per.addr);system("pause");return 0;
}
7.线程分离属性:线程消亡,自动回收空间
pthread_attr_init()函数:初始化一个attr的变量
int pthread_attr_init(pthread_attr_t *attr);
参数:attr,需要变量来接受初始值。
返回值:成功返0,失败返非0 (错误)
pthread_attr_destroy()函数:销毁attr变量
int pthread_attr_destroy(pthread_attr_t *attr);
参数:attr,属性变量
返回值:成功返0,失败返非0(错误)
pthread_attr_setdetachstate()函数:把一个线程设置成相应的属性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
参数:attr,属性变量,有init函数初始化他。
detachstate:有2个可选值,
PTHREAD_CREATE_DETACHED
pthread_attr_deatch()函数:设置分离属性:线程id号,填自己的id
8.线程清理函数
pthread_cleanup_push:注册一个线程清理函数
void pthread_cleanup_push(void (*routine)(void *), void *arg);
参数:routine,线程清理函数的入口
arg,清理函数的参数。
pthread_cleanup_pop:调用清理函数
void pthread_cleanup_pop(int execute);
参数:execute,非0 执行清理函数
0 ,不执行清理
注:pthread_cleanup_push与pthread_cleanup_pop函数需成对出现;
四、线程与进程的区别
进程可以启动线程,线程也可启动进程(进程中的多线程变为单线程)
1、资源:
(1)线程比进程多了共享资源;
(2)线程又具有部分私有资源;
(3)进程间只有私有资源没有共享资源。
2、空间:
(1)进程空间独立(写时复制),不能直接通信;
(2)线程可以共享空间(栈区默认不共享),可以直接通信。
3、不同点:
(1)创建开销不一样,进程创建需要3G空间,线程创建只需要8M,线程并发度高于进
程;
(2)进程变量不共享;
(3)进程切换(复杂)需要的缓存空间多,线程切换栈区,PC指针也发生变化;
(4)进程可申请到硬件资源;
4、稳定性差异:
(1)线程稳定性差(其中一个线程崩溃或严重异常,进程直接结束);
(2)项目任务复杂,用进程做;简单小任务,用线程做;
5、共同点:都能并发。
6、考虑用线程还是进程的两个准则:
(1)稳定性;
(2)看资源够不够用(够用用线程)。
线程的优缺点
优点:比多进程节省资源,可以共享变量。
缺点:1.线程和进程相比,稳定性,稍微差些
2.线程的调试gdb,相对麻烦些,因为并发