为什么使用线进程,什么是线程
操作系统的设计使用多线程编程的弊端多线程编程够弥补上面的问题多线程编程的弊端多线程多进程使用场景什么是线程线程的特点 串行 并发 并行
串行并发并行形象生动 perror和strerror创建线程终止线程回收线程取消线程
取消一个进程取消状态以及类型取消点线程可取消性的检测 分离线程注册线程清理处理函数线程属性
线程栈属性分离状态属性 线程安全
线程栈可重入函数线程安全函数一次性初始化线程特有数据线程局部存储
为什么使用线进程,什么是线程 操作系统的设计使用多线程编程的弊端以多进程形式,允许多个任务同时运行;以多线程形式,允许单个任务分成不同的部分运行;提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。
多线程编程够弥补上面的问题进程间切换开销大,通常对于一些中小型应用程序来说不划算。进程间通信较为麻烦。 每个进程都在各自的地址空间中、相互独立、隔离,处在于不同的地址空间
多线程编程的弊端同一进程的多个线程间切换开销比较小。同一进程的多个线程间通信容易。 它们共享了进程的地址空间,所以它们都是在同一个地址空间 中,通信容易。线程创建的速度远大于进程创建的速度。多线程在多核处理器上更有优势!
多线程多进程使用场景线程也有它的缺点、劣势, 譬如多线程编程难度高,对程序员的编程功底要求比较高,因为在多线程环境下 需要考虑很多的问题。例如线程安全问题、信号处理的问题等, 编写与调试一个多线程程序比单线程程序困 难得多。
什么是线程对资源的管理和保护要求高,不限制开销和效率时,使用多进程。要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。多进程编程通常会用在一些大型应用程序项目中,譬如网络服务器应用程序,在中小型应用程序中用的比较少。
线程的特点线程是参与系统调度的最小单位。进程是资源分配的最小单位一个进程中可以创建多个线程, 多个线程实现并发运行, 每个线程执行不同的任务。. 譬如某应用程序设计了两个需要并发运行的任务 task1 和 task2,可将两个不同的任务分别放置在两个线程中。有点类似freertos里面的任务
串行 并发 并行 串行线程是程序最基本的运行单位,而进程不能运行, 真正运行的是进程中的线程。 当启动应用程序后,系统就创建了一个进程,可以认为进程仅仅是一个容器, 它包含了线程运行所需的数据结构、环境变量等信息。可并发执行。同一进程的多个线程之间可并发执行,在宏观上实现同时运行的效果;共享进程资源。
并发它指的是一种顺序执行,譬如先完成 task1,接着做 task2、直到完task2,然 后做 task3、直到完成,必须要完成上一件事才能去做下一件事,只 有一个执行单元,这就是串行运行。
并行每个任务执行一段时间,时间一到则切换执行下一个任务,依次这样轮训(交叉/交替执行) ,这就是并发运行。
形象生动并行与串行则截然不同,并行指的是可以并排/并列执行多个任务, 这样的系统,它通常有多个执行单元, 所以可以实现并行运行,譬如并行运行 task1、 task2、 task3。并行运行并不一定要同时开始运行、同时结束运行,只需满足在某一个时间段上存在多个任务被多个执行单元同时在运行着。
perror和strerror你吃饭吃到一半,电话来了,你一直到吃完了以后才去接电话,这就说明不支持并发也不支持并行,仅仅只是串行。你吃饭吃到一半,电话来了,你停下吃饭去接了电话,电话接完后继续吃饭,这说明你支持并发。你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
⚫ 串行:一件事、一件事接着做
⚫ 并发:交替做不同的事;
⚫ 并行:同时做不同的事。
在库函数中有个errno的全局变量,每个errno的值对应错误的类型。当我们调用某些函数出错时,该函数就设置了errno的值,perror就将errno值对应的错误类型打印出来(这也是perror要紧跟着函数调用的原因);而在另外一些函数中,函数出错并不设置errno的值,而是通过返回错误类型对应的值,来得到错误的类型,在这种情况下,我们就要使用strerror(线程就是这类)
不知道哪一个函数用哪一个可以man一下这个函数,搜索return value
open函数:这类可以使用perror
pthread_create函数:这类就不能使用perror
1.首先通过 / 查找,如:/return value 2.然后用n查找下一个,用N查找上一个创建线程
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); #include#include #include #include #include #include //测试新进程id与函数得到id是否匹配 pthread_t tid; static void *new_thread_start(void *arg) { printf("新线程:进程ID<%d> 全局线程ID<%lu> 函数线程ID<%lu>n", getpid(), tid,pthread_self()); return (void*)0; //执行 return 语句终止线程 } int main(void) { int ret; ret = pthread_create(&tid, NULL, new_thread_start, NULL); if (ret) { //在标准 I/O 中,可以使用 stdin、 stdout、 stderr 来表示标准输入、标准输出和标准错误。 //fprintf()可将格式化数据写入到由 FILE 指针指定的文件中,譬如将字符串“Hello World”写入到标准错误: fprintf(stderr,"pthread_create error:%sn",strerror(ret)); exit(-1); } printf("主线程:进程ID<%d> 线程ID<%lu>n", getpid(), pthread_self()); sleep(1); //延时,new_thread_start线程函数执行完等待一会,如果进程中的任意线程调用 exit()、 _exit()或者_Exit(),那么将会导致整个进程终止,这里需要注意!这里的延时也不会执行 exit(0); }
终止线程编译时出现了错误,提示“对‘pthread_create’未定义的引用”,示例代码确实已经包含了
头文件,报错是出现在程序代码链接时、而并非是编译过程,所以可知这是链接库的文件, 使用-l 选项指定链接库 pthread,原因在于 pthread 不在 gcc 的默认链接库中,所以需要手动指定。
// void pthread_exit(void *retval); #include#include #include #include #include static void *new_thread_start(void *arg) { puts("新线程"); //睡眠1秒钟,给主线程退出时间 sleep(1); puts("新线程退出"); //退出新线程 pthread_exit(NULL); puts("新线程我没了"); } int main(void) { pthread_t tid; int ret; ret = pthread_create(&tid, NULL, new_thread_start, NULL); if (ret) { fprintf(stderr,"pthread_create error:%sn",strerror(ret)); exit(-1); } puts("主线程退出"); //退出主线程 pthread_exit(NULL); puts("主线程我没了"); exit(0); }
回收线程pthread_exit()终止之后,整个进程并没有结束,而新线程还在继续运行。
//int pthread_join(pthread_t thread, void **retval); #include#include #include #include #include #include static void *new_thread_start(void *arg) { puts("新线程"); //睡眠1秒钟,给主线程退出时间 sleep(2); puts("新线程退出"); //退出新线程 pthread_exit((void*)10); } int main(void) { pthread_t tid; void *tret; int ret; //申请一个线程 ret = pthread_create(&tid, NULL, new_thread_start, NULL); if (ret) { perror("pthread_create error"); exit(-1); } //回收一个线程 ret = pthread_join(tid,&tret); if (ret) { fprintf(stderr,"pthread_join error:%sn",strerror(ret)); exit(-1); } printf("新线程终止, code=%ldn", (long)tret); exit(0); }
取消线程 取消一个进程主线程中调用pthread_join()阻塞等待新线程终止,新线程终止后,pthread_join()返回,将目标线程的退出码保存在*tret 所指向的内存中。
// int pthread_cancel(pthread_t thread); #include#include #include #include #include #include static void *new_thread_start(void *arg) { puts("新线程死循环执行"); for (;;) //这里必须有睡眠函数,不然无法取消线程,这是个取消点函数 sleep(1); return (void *)0; } int main(void) { pthread_t tid; void *tret; int ret; //申请一个线程 ret = pthread_create(&tid, NULL, new_thread_start, NULL); if (ret) { fprintf(stderr,"pthread_create error:%sn",strerror(ret)); exit(-1); } //取消一个线程 ret = pthread_cancel(tid); if (ret) { fprintf(stderr, "pthread_cancel error:%sn", strerror(ret)); exit(-1); } //回收一个线程 ret = pthread_join(tid, &tret); if (ret) { fprintf(stderr, "pthread_join error:%sn", strerror(ret)); exit(-1); } printf("新线程终止, code=%ldn", (long)tret); exit(0); }
取消状态以及类型当主线程发送取消请求之后,新线程便退出了,而且退出码为-1,也就是 PTHREAD_CANCELED。注意死循环里的sleep函数,没有该函数将无法取消,有该函数也就是有一个取消点。
// int pthread_setcancelstate(int state, int *oldstate); #include取消点#include #include #include #include #include static void *new_thread_start(void *arg) { puts("新线程设置为不可取消状态"); pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); for (;;) { puts("新线程死循环执行"); sleep(2); } return (void *)0; } int main(void) { pthread_t tid; void *tret; int ret; //申请一个线程 ret = pthread_create(&tid, NULL, new_thread_start, NULL); if (ret) { fprintf(stderr, "pthread_cancel error:%sn", strerror(ret)); exit(-1); } //取消一个线程 ret = pthread_cancel(tid); if (ret) { fprintf(stderr, "pthread_cancel error:%sn", strerror(ret)); exit(-1); } //回收一个线程 ret = pthread_join(tid, &tret); if (ret) { fprintf(stderr, "pthread_join error:%sn", strerror(ret)); exit(-1); } printf("新线程终止, code=%ldn", (long)tret); exit(0); }
// int pthread_setcanceltype(int type, int *oldstate); #include线程可取消性的检测#include #include #include #include #include static void *new_thread_start(void *arg) { puts("新线程设置为取消点到来时取消,也就是调用sleep函数puts函数等"); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL); for (;;) { puts("新线程死循环执行"); sleep(2); } return (void *)0; } int main(void) { pthread_t tid; void *tret; int ret; //申请一个线程 ret = pthread_create(&tid, NULL, new_thread_start, NULL); if (ret) { fprintf(stderr, "pthread_create error:%sn", strerror(ret)); exit(-1); } //取消一个线程 ret = pthread_cancel(tid); if (ret) { fprintf(stderr, "pthread_cancel error:%sn", strerror(ret)); exit(-1); } //回收一个线程 ret = pthread_join(tid, &tret); if (ret) { fprintf(stderr, "pthread_join error:%sn", strerror(ret)); exit(-1); } printf("新线程终止, code=%ldn", (long)tret); exit(0); }
//void pthread_testcancel(void); #include#include #include #include #include #include static void *new_thread_start(void *arg) { puts("新线程死循环执行"); for (;;) { //不调用该函数线程无法取消,调用该函数就会产生一个取消点进行取消检测 pthread_testcancel(); } return (void *)0; } int main(void) { pthread_t tid; void *tret; int ret; //申请一个线程 ret = pthread_create(&tid, NULL, new_thread_start, NULL); if (ret) { fprintf(stderr, "pthread_create error:%sn", strerror(ret)); exit(-1); } //取消一个线程 ret = pthread_cancel(tid); if (ret) { fprintf(stderr, "pthread_cancel error:%sn", strerror(ret)); exit(-1); } //回收一个线程 ret = pthread_join(tid, &tret); if (ret) { fprintf(stderr, "pthread_join error:%sn", strerror(ret)); exit(-1); } printf("新线程终止, code=%ldn", (long)tret); exit(0); }
分离线程死循环里没有取消点函数,那该线程需要调用生成取消点函数来取消
//int pthread_detach(pthread_t thread); #include注册线程清理处理函数#include #include #include #include #include static void *new_thread_start(void *arg) { int ret; ret=pthread_detach(pthread_self()); if (ret) { fprintf(stderr,"pthread_detach error:%sn",strerror(ret)); } puts("新线程分离开始执行"); sleep(2); puts("新线程终止"); pthread_exit((void*)10); } int main(void) { pthread_t tid; void *tret; int ret; //申请一个线程 ret = pthread_create(&tid, NULL, new_thread_start, NULL); if (ret) { fprintf(stderr,"pthread_create error:%sn",strerror(ret)); exit(-1); } //确保新线程执行了分离函数 sleep(1); //回收一个线程 ret = pthread_join(tid, &tret); if (ret) { fprintf(stderr,"pthread_join error:%sn",strerror(ret)); sleep(3); printf("新线程终止, code=%ldn", (long)tret); pthread_exit(NULL); } }
//void pthread_cleanup_push(void (*routine)(void *), void *arg); //void pthread_cleanup_pop(int execute); #include线程属性 线程栈属性#include #include #include #include #include static void cleanup(void *arg) { printf("cleanup: %sn", (char *)arg); } static void *new_thread_start(void *arg) { puts("新线程--start run"); pthread_cleanup_push(cleanup, "第 1 次调用"); pthread_cleanup_push(cleanup, "第 2 次调用"); pthread_cleanup_push(cleanup, "第 3 次调用"); pthread_cleanup_pop(1);//传入非零值,清理函数工作,执行最顶层的清除函数 puts("==================="); sleep(2); pthread_exit((void *)0); //线程终止, 如果这里调用return,发现并不会执行清理函数。 //线程退出开始执行 pthread_cleanup_pop(0); pthread_cleanup_pop(0); } int main(void) { pthread_t tid; void *tret; int ret; //申请一个线程 ret = pthread_create(&tid, NULL, new_thread_start, NULL); if (ret) { fprintf(stderr,"pthread_create error:%sn",strerror(ret)); exit(-1); } //回收一个线程 ret = pthread_join(tid, &tret); if (ret) { fprintf(stderr, "pthread_join error:%sn", strerror(ret)); exit(-1); } printf("新线程终止, code=%ldn", (long)tret); exit(0); }
//int pthread_attr_init(pthread_attr_t *attr); //int pthread_attr_destroy(pthread_attr_t *attr); //int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); //int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize); //int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); //int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize); //int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr); //int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr); #include分离状态属性#include #include #include #include #include static void *new_thread_start(void *arg) { puts("新线程--start run"); pthread_exit((void *)0); } int main(void) { pthread_t tid; pthread_attr_t attr; void *stackaddr; size_t stacksize;//这里不能定义为指针变量 定义为指针变量,执行会报错Segmentation fault (core dumped) void *tret; int ret; //线程属性函数初始化 pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 4096); //申请一个线程 ret = pthread_create(&tid, &attr, new_thread_start, NULL); if (ret) { fprintf(stderr,"pthread_create error:%sn",strerror(ret)); exit(-1); } // pthread_attr_getstack(&attr,&stackaddr,&stacksize); //栈起始地址:(nil) 栈大小:0 // pthread_attr_getstackaddr(&attr, &stackaddr); // pthread_attr_getstacksize(&attr, &stacksize); 栈起始地址:(nil) 栈大小:8388608 // printf("栈起始地址:%p 栈大小:%ldn",stackaddr,stacksize); //回收一个线程 ret = pthread_join(tid, &tret); if (ret) { fprintf(stderr, "pthread_join error:%sn", strerror(ret)); exit(-1); } printf("新线程终止, code=%ldn", (long)tret); pthread_attr_destroy(&attr); exit(0); }
// int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); // int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); #include线程安全 线程栈#include #include #include #include #include static void *new_thread_start(void *arg) { puts("新线程--start run"); pthread_exit(NULL); } int main(void) { pthread_t tid; pthread_attr_t attr; void *tret; int ret; //线程属性函数初始化 pthread_attr_init(&attr); //设置为分离状态,不可回收 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //申请一个线程 ret = pthread_create(&tid, &attr, new_thread_start, NULL); if (ret) { fprintf(stderr, "pthread_create error:%sn", strerror(ret)); exit(-1); } //等待分离线程执行完 sleep(1); pthread_attr_destroy(&attr); exit(0); }
#include可重入函数#include #include #include #include #include #define PTHREAD_SIZE 5 static void *new_thread_start(void *arg) { int number = *((int *)arg); unsigned long tid = pthread_self(); printf("第%d号线程,线程ID<%ld>n", number, tid); pthread_exit((void *)number); } int main(void) { pthread_t tid[PTHREAD_SIZE]; int pthread_number[PTHREAD_SIZE] = {1, 2, 3, 4, 5}; int i; void *tret; int ret; //申请5个线程 for (i = 0; i < PTHREAD_SIZE; i++) { ret = pthread_create(&tid[i], NULL, new_thread_start, (void *)&pthread_number[i]); if (ret) { fprintf(stderr, "%d pthread_create error:%sn", i, strerror(ret)); exit(-1); } } //回收5个线程 for (i = PTHREAD_SIZE - 1; i >= 0; i--) { ret = pthread_join(tid[i], &tret); if (ret) { fprintf(stderr, "%d pthread_join error:%sn", i, strerror(ret)); exit(-1); } printf("回收线程%ldn", (long)tret); } exit(0); }
线程安全函数绝对可重入函数的特点:
⚫ 函数内所使用到的变量均为局部变量,换句话说,该函数内的操作的内存地址均为本地栈地址;
⚫ 函数参数和返回值均是值类型;
⚫ 函数内调用的其它函数也均是绝对可重入函数。
// int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); #include线程特有数据#include #include #include #include #include #define PTHREAD_SIZE 5 static pthread_once_t once_control = PTHREAD_ONCE_INIT; static void initialize_once(void) { printf("被线程ID<%ld>调用n", pthread_self()); } static void func(void) { pthread_once(&once_control, initialize_once); //执行一次性初始化函数 printf("函数 func 执行完毕.n"); } static void *new_thread_start(void *arg) { printf("第%d号线程,线程ID<%ld>n", *((int *)arg), pthread_self()); func(); pthread_exit(NULL); } int main(void) { pthread_t tid[PTHREAD_SIZE]; int pthread_number[PTHREAD_SIZE] = {1, 2, 3, 4, 5}; int i; int ret; //申请5个线程 for (i = 0; i < PTHREAD_SIZE; i++) { ret = pthread_create(&tid[i], NULL, new_thread_start, (void *)&pthread_number[i]); if (ret) { fprintf(stderr, "%d pthread_create error:%sn", i, strerror(ret)); exit(-1); } } //回收5个线程 for (i = PTHREAD_SIZE - 1; i >= 0; i--) { ret = pthread_join(tid[i], NULL); if (ret) { fprintf(stderr, "%d pthread_join error:%sn", i, strerror(ret)); exit(-1); } } exit(0); }
#define _GNU_SOURCE #include线程局部存储#include #include #include #define MAX_ERROR_LEN 256 static pthread_once_t once = PTHREAD_ONCE_INIT; static pthread_key_t strerror_key; static void destructor(void *buf) { free(buf); //释放内存 } static void create_key(void) { if (pthread_key_create(&strerror_key, destructor)) pthread_exit(NULL); } static char *strerror(int errnum) { char *buf; if (pthread_once(&once, create_key)) pthread_exit(NULL); buf = pthread_getspecific(strerror_key); if (NULL == buf) { //首次调用 my_strerror 函数,则需给调用线程分配线程私有数据 buf = malloc(MAX_ERROR_LEN); //分配内存 if (NULL == buf) pthread_exit(NULL); if (pthread_setspecific(strerror_key, buf)) pthread_exit(NULL); } if (errnum < 0 || errnum >= _sys_nerr || NULL == _sys_errlist[errnum]) snprintf(buf, MAX_ERROR_LEN, "Unknown error %d", errnum); else { strncpy(buf, _sys_errlist[errnum], MAX_ERROR_LEN - 1); buf[MAX_ERROR_LEN - 1] = ' '; //终止字符 } return buf; }
#include#include #include #include static __thread char buf[100]; static void *thread_start(void *arg) { strcpy(buf, "Child Threadn"); printf("子线程: buf (%p) = %s", buf, buf); pthread_exit(NULL); } int main(int argc, char *argv[]) { pthread_t tid; int ret; strcpy(buf, "Main Threadn"); if (ret = pthread_create(&tid, NULL, thread_start, NULL)) { fprintf(stderr, "pthread_create error: %dn", ret); exit(-1); } if (ret = pthread_join(tid, NULL)) { fprintf(stderr, "pthread_join error: %dn", ret); exit(-1); } printf("主线程: buf (%p) = %s", buf, buf); exit(0); }



