一、线程的概述进程是系统分配资源的基本单位线程是cpu执行调度的基本单位线程是轻量级的进程LWPlight weight process在Linux环境下线程的本质仍是进 程进程必须至少包含一个线程。线程 依赖于 进程线程共享进程的资源进程结束 当前进程的所有线程 都将立即结束。线程共享资源文件描述符表每种信号的处理方式当前工作目录4) 用户ID和组ID 内存地址空间 (.text/.data/.bss/heap/共享库)线程非共享资源线程id处理器现场和栈指针(内核栈)独立的栈空间(用户空间栈)errno变量信号屏蔽字调度优先级线程的优缺点:优点 1提高程序并发性2开销小 3数据通信、共享数据方便缺点 1库函数不稳定 2调试、编写困难、gdb不支持 3对信号支持不好优点相对 突出缺点均不是硬伤。Linux下由于实现方法导致进程、线程差别不是很大。二、线程的API1、查看线程号#include pthread.hpthread_t pthread_self(void);功能 获取线程号。参数 无返回值 调用线程的线程 ID2、创建线程#includepthread.hint pthread_create(pthread_t *thread,const pthread_attr_t *attr, void *(*start_routine)(void *),void *arg );功能 创建一个线程参数 thread线程标识符地址。 attr线程属性结构体地址通常设置为 NULL。 start_routine线程函数的入口地址。 arg传给线程函数的参数。返回值 成功0 失败非 0案例1创建线程每个线程有独立的线程函数#include stdio.h #include pthread.h #include unistd.h void *pthread_fun01(void *arg) { int i 0; while (1) { printf(%s‐‐‐‐‐‐‐i%d\n, (char *)arg, i); sleep(1); } return NULL; } void *pthread_fun02(void *arg) { int i 0; while (1) { printf(%s‐‐‐‐‐‐‐i%d\n, (char *)arg, i); sleep(1); } return NULL; } int main(int argc, char const *argv[]) { //创建两个线程 pthread_t tid1, tid2; pthread_create(tid1, NULL, pthread_fun01, 任务A); pthread_create(tid2, NULL, pthread_fun02, 任务B); getchar(); return 0; }案例2创建线程每个线程共用的同一线程函数#include stdio.h #include pthread.h #include unistd.h void *pthread_fun(void *arg) { int i 0; while (1) { printf(%s‐‐‐‐‐‐‐i%d\n, (char *)arg, i); sleep(1); } return NULL; } int main(int argc, char const *argv[]) { //创建两个线程 pthread_t tid1, tid2; pthread_create(tid1, NULL, pthread_fun, 任务A); pthread_create(tid2, NULL, pthread_fun, 任务B); getchar(); return 0; }3、回收线程资源阻塞pthread_join函数带阻塞#includepthread.hint pthread_join(pthread_t thread, void **retval);功能等待线程结束此函数会阻塞并回收线程资源类似进程的 wait() 函数。如果线程 已经结束那么该函数会立即返回。参数 thread被等待的线程号。 retval用来存储线程退出状态的指针的地址返回值 成功0 失败非 0例1回收线程资源#include stdio.h #include pthread.h #include unistd.h void *pthread_fun01(void *arg) { int i 0; for (; i 5; i) { printf(%s‐‐‐‐‐‐‐i%d\n, (char *)arg, i); sleep(1); } return NULL; } void *pthread_fun02(void *arg) { int i 0; for (; i 3; i) { printf(%s‐‐‐‐‐‐‐i%d\n, (char *)arg, i); sleep(1); } return NULL; } int main(int argc, char const *argv[]) { //创建两个线程 pthread_t tid1, tid2; pthread_create(tid1, NULL, pthread_fun01, 任务A); pthread_create(tid2, NULL, pthread_fun02, 任务B); //回收线程资源(带阻塞的) pthread_join(tid1, NULL); printf(tid1结束了\n); pthread_join(tid2, NULL); printf(tid2结束了\n); return 0; }例2回收线程资源 并获取线程返回值的值#include stdio.h #include pthread.h #include unistd.h void *pthread_fun01(void *arg) { int i 0; for (; i 5; i) { printf(%s‐‐‐‐‐‐‐i%d\n, (char *)arg, i); sleep(1); } return (void *)任务A; } void *pthread_fun02(void *arg) { int i 0; for (; i 3; i) { printf(%s‐‐‐‐‐‐‐i%d\n, (char *)arg, i); sleep(1); } return (void *)任务B; } int main(int argc, char const *argv[]) { //创建两个线程 pthread_t tid1, tid2; pthread_create(tid1, NULL, pthread_fun01, 任务A); pthread_create(tid2, NULL, pthread_fun02, 任务B); //回收线程资源(带阻塞的) void *p1 NULL; pthread_join(tid1, p1); printf(tid1结束了返回值为%s\n, (char *)p1); pthread_join(tid2, p1); printf(tid2结束了, 返回值为%s\n, (char *)p1); return 0; }4、pthread_detach分离线程不阻塞将线程的回收工作分离出去 线程结束时线程由系统回收资源。#includepthread.hint pthread_detach(pthread_t thread);功能 使调用线程与当前进程分离分离后不代表此线程不依赖与当前进程线程分离的目的 是将线程资源的回收工作交由系统自动来完成也就是说当被分离的线程结束之后系统会 自动回收它的资源。所以此函数不会阻塞。参数 thread线程号。返回值: 成功0 失败非0#include stdio.h #include pthread.h #include unistd.h void *pthread_fun01(void *arg) { int i 0; for (; i 5; i) { printf(%s‐‐‐‐‐‐‐i%d\n, (char *)arg, i); sleep(1); } return (void *)任务A; } int main(int argc, char const *argv[]) { //创建两个线程 pthread_t tid1, tid2; pthread_create(tid1, NULL, pthread_fun01, 任务A); //线程分离(不带阻塞) pthread_detach(tid1); //主函数 也是一个线程 int i 0; while (1) { printf(%s‐‐‐‐‐‐‐i%d\n, 任务B, i); sleep(1); } return 0; }三、线程的取消和退出1、线程的取消取消自己、也可以取消当前进程的其他线程。#includepthread.hint pthread_cancel(pthread_t thread);功能 杀死(取消)线程参数 thread : 目标线程ID。返回值 成功0 失败出错编号杀死线程也不是立刻就能完成必须要到达取消点。 取消点是线程检查是否被取消并 按请求进行动作的一个位置。通常是一些系统调用。#include stdio.h #include pthread.h #include unistd.h void *pthread_fun01(void *arg) { int i 0; while (1) { printf(%s‐‐‐‐‐‐‐i%d\n, (char *)arg, i); sleep(1); //取消点 } return NULL; } int main(int argc, char const *argv[]) { //创建两个线程 pthread_t tid1; pthread_create(tid1, NULL, pthread_fun01, 任务A); //线程分离(不带阻塞) pthread_detach(tid1); printf(5秒后结束任务A\n); sleep(5); pthread_cancel(tid1); getchar(); return 0; }2、线程退出#includepthread.hvoid pthread_exit(void *retval);功能 退出调用线程。一个进程中的多个线程是共享该进程的数据段因此通常线程退出后 所占用的资源并不会释放。参数 retval存储线程退出状态的指针。返回值无#include stdio.h #include pthread.h #include unistd.h void *pthread_fun01(void *arg) { int i 0; while (1) { printf(%s‐‐‐‐‐‐‐i%d\n, (char *)arg, i); if (i 5) pthread_exit(NULL); //线程结束 sleep(1); } return NULL; } int main(int argc, char const *argv[]) { //创建线程 pthread_t tid1; pthread_create(tid1, NULL, pthread_fun01, 任务A); pthread_join(tid1, NULL); printf(任务A结束了\n); return 0; }四、线程的属性1、结构体typedef struct { int etachstate; //线程的分离状态 int schedpolicy; //线程调度策略 struct sched_param schedparam; //线程的调度参数 int inheritsched; //线程的继承性 int scope; //线程的作用域 size_t guardsize; //线程栈末尾的警戒缓冲区大小 int stackaddr_set; //线程的栈设置 void* stackaddr; //线程栈的位置 size_t stacksize; //线程栈的大小 } pthread_attr_t;使用线程的属性 完成线程分离以前的方案pthread_t tid1; pthread_create(tid1,NULL, pthread_fun01, 任务A); //线程分离(不带阻塞) pthread_detach(tid1);如果线程先结束 pthread_detach后执行 就存在问题。现在的方案创建线程的时候 通过线程属性 设置线程分离就定义保存 先分离 后执行线 程解决了上面的问题 初始化的函数为pthread_attr_init这个函数必须在pthread_create函数之前调用。初始化线程属性函数intpthread_attr_init(pthread_attr_t *attr);函数返回值成功0失败错误号销毁线程属性所占用的资源函数intpthread_attr_destroy(pthread_attr_t *attr);函数返回值成功0失败错误号线程分离状态的函数 设置线程属性分离or非分离int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);获取程属性分离or非分离int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstat);参数attr已初始化的线程属性detachstate分离状态PTHREAD_CREATE_DETACHED分离线程PTHREAD_CREATE_JOINABLE非分离线程案例1设置线程分离、修改线程的栈的地址和大小#include pthread.h #include stdio.h #include unistd.h #include stdlib.h //#define SIZE 0x100000 #define SIZE 128 void *th_fun(void *arg) { while (1) sleep(1); } int main(void) { pthread_t tid; int err, detachstate, i 1; pthread_attr_t attr; size_t stacksize; void *stackaddr; //初始化线程属性 pthread_attr_init(attr); //设置线程的属性值为分离状态 pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED); #if 1 while (1) { stackaddr malloc(SIZE); if (stackaddr NULL) { perror(malloc); exit(1); } stacksize SIZE; //将栈的地址 和栈的大小设置到线程属性值里 pthread_attr_setstack(attr, stackaddr, stacksize); //将上面设置好的线程属性值 赋值到 创建的线程中 err pthread_create(tid, attr, th_fun, NULL); if (err ! 0) { printf(%s\n, strerror(err)); exit(1); } printf(%d\n, i); } #endif pthread_attr_destroy(attr); return 0; }例2设置线程分离#include pthread.h #include stdio.h #include unistd.h #include stdlib.h //#define SIZE 0x100000 #define SIZE 128 void *th_fun(void *arg) { while (1) { printf(任务A\n); sleep(1); } } int main(void) { pthread_t tid; pthread_attr_t attr; //初始化线程属性 pthread_attr_init(attr); //设置线程的属性值为分离状态 pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED); //创建线程 pthread_create(tid, attr, th_fun, NULL); pthread_attr_destroy(attr); getchar(); return 0; }五、创建多线程#include pthread.h #include stdio.h #include unistd.h #include stdlib.h #include string.h typedef struct { char task_name[32]; int time; } MSG; void *deal_fun(void *arg) { MSG msg *(MSG *)arg; int i 0; for (i msg.time; i 0; i‐‐) { printf(%s剩余时间%d\n, msg.task_name, i); sleep(1); } return NULL; } int main(int argc, char const *argv[]) { while (1) { MSG msg; printf(输入新增的任务名:); fgets(msg.task_name, sizeof(msg.task_name), stdin); msg.task_name[strlen(msg.task_name) ‐ 1] 0; printf(输入运行时间:); scanf(%d, msg.time); getchar(); //获取换行符 pthread_t tid; pthread_create(tid, NULL, deal_fun, (void *)msg); pthread_detach(tid); //线程分离 } return 0; }