多线程API - 小白如何快速上手并发编程
-
线程的基本概念
- 一.线程和进程的区别(linux环境下)
- 进程是资源管理的最小单位,线程是系统执行的最小单位
- 线程和进程都有对应的PCB和TCB,在linux内核中创建线程和进程使用的底层函数和进程一样,都是clone
- 进程可以蜕化成线程,一个进程可以有一个至多个线程
- 进程有自己独享的0-4G虚拟地址空间,而线程共享同一进程的地址空间
- 二.线程有共享也有独占
- 线程共享的资源有:
- 1.文件描述符表
- 2.每种信号的处理方式
- 3.当前工作目录
- 4.用户ID和组ID
- 5.内存地址空间 (.text/.data/.bss/heap/共享库)
- 线程独占的资源有:
- 1.线程id
- 2.计数器和栈空间(内核栈和用户空间栈)
- 3.信号屏蔽字
- 4.errno变量
- 5.调度优先级
- 线程共享的资源有:
- 三.线程的优缺点
- 优点
- 1.提高程序并发性
- 2.开销小
- 3.数据通信、共享数据方便
- 缺点
- 1.线程所有操作函数 pthread_* 是库函数,而非系统调用,不够稳定
- 2.编写调试困难,不支持gdb
- 3.对信号支持性差
- 优点
- 一.线程和进程的区别(linux环境下)
-
使用线程的API
- 线程常见的函数有以下几种
- pthread_self函数 - 获取线程id
- pthread_create函数 - 创建一个新线程
- pthread_exit函数 - 将单个线程退出
- pthread_join函数 - 阻塞等待线程退出并获取线程退出状态
- pthread_cancel函数 - 杀死(取消)线程
- pthread_detach函数 - 实现线程分离
- pthread_equal函数 - 比较线程id是否相等(系统留用,看以后pthread_t是否改为结构体)
- 线程API和进程API对比
- pthread_self - getpid
- pthread_create - fork
- pthread_exit - exit
- pthread_join - wait
- pthread_cancel - kill
- 线程常见的函数有以下几种
-
线程控制原语
-
pthread_t pthread_self(void); ---------使用见线程实例1
-
功能 : 获取线程id
-
返回值:成功:0; 失败:NULL
-
线程ID:pthread_t类型,本质:在Linux下为无符号整数(%lu),可以理解为typedef unsigned long int pthread_t;
-
notes
-
线程id是进程内识别不同线程的标志,也意味着两个进程间有允许使用相同的线程id
-
在获取线程id时,避免使用全局变量。在主线程可通过pthread_create第一个参数传出,在子线程通过调用pthread_self()得到id.
-
-
-
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); -----使用见线程实例1
-
功能 :创建一个新线程
-
返回值:成功:0; 失败:错误号 -----Linux环境下,所有线程特点,失败均直接返回错误号。
-
parameters
-
参数1:传出参数,保存系统为我们分配好的线程ID
-
参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
-
参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
-
参数4:线程主函数执行期间所使用的参数==,若线程需要多个参数,可传入结构体变量==
-
-
-
-
void pthread_exit(void *retval); ------ 使用见线程实例3
-
功能 : 将单个线程退出
-
parameter
- retval表示线程退出状态,通常传NULL
-
notes
-
不能使用exit退出指定的线程,因为exit是退出进程的,会将该进程所有线程退出
-
线程的三种退出方式
- return
- pthread_exit()
- pthread_cancel()
-
-
-
-
int pthread_join(pthread_t thread, void **retval); ------使用见线程实例4
-
功能 : 阻塞等待线程退出并获取线程退出状态
-
parameters
- 参数1 : 需要等待的线程id
- 参数2 : 获取线程id退出状态
-
notes
-
如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值.
-
如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED(一般为-1)。
-
如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传pthread_exit的参数
-
如果对线程退出状态没有兴趣,可以对retval传空。
-
-
-
int pthread_cancel(pthread_t thread); ------使用见线程实例4
-
功能 : 杀死(取消)线程
-
成功:0;失败:错误号
-
notes
- 线程取消不是立马执行的,需要一定的时间。需要线程进入系统调用creat,open,pause,close,read,write等,才可退出,若线程中没有系统调用,需要添加pthread_testcancel()函数断点,否则线程死循环
-
-
int pthread_detach(pthread_t thread); ------使用见线程实例5
-
功能 : 实现线程分离
-
返回值:0;失败:错误号
-
notes
- 分离态线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态,避免僵尸线程。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL错误(一般为22)。
-
-
int pthread_equal(pthread_t t1, pthread_t t2);//几乎不用
- 功能 : 获取线程id
-
-
线程实例
-
1.创建一个子线程,子线程打印出自己的线程id
#include <stdio.h> #include <unistd.h> #include <pthread.h> void *thread1(void *arg) { printf("my thread_id is %lu\n",pthread_self()); return NULL; } int main(int args, char *argv[]) { pthread_t tid;//pthread is equal to unsigned long in linux pthread_create(&tid, NULL, thread1, NULL);//第一个NULL表示默认属性,第二个是线程执行时所需要的参数 sleep(1);//wait thread end return 0; }
- 编译
- 运行
-
2.循环创建多个子线程,并使用pthread_exit退出其中i = 3的线程
#include <stdio.h> #include <unistd.h> #include <pthread.h> void *thread(void *arg) { int i = 0; i = (int)arg; if(3 == i)//3 thread exit { pthread_exit(NULL); } sleep(i);//keep thread write order printf("I am %d thead, my thread id is %lu\n", i, pthread_self()); } int main(int args, char *arg[]) { pthread_t tid = 0; int i = 0; for(i=0; i<5; i++) { pthread_create(&tid, NULL, thread, (void*)i); } sleep(i);//wait thread end return 0; }
- 编译
- 运行
-
3.创建多个线程并使用pthread_exit退出,打印出join保存的线程退出状态
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> typedef struct node { int a; int b; }LNODE; void *thread(void *arg) { LNODE *node1; node1 = (LNODE*)malloc(sizeof(struct node)); if(NULL == node1) { printf("no enough memory malloc\n "); return NULL; } node1->a = 100; node1->b = 200; pthread_exit((void *)node1);//如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数 } int main(int args,char *argv[]) { pthread_t tid = 0; LNODE *node2 = NULL; pthread_create(&tid, NULL, thread, NULL); pthread_join(tid, (void**)&node2); printf("node->a is %d, node->b = %d\n", node2->a, node2->b); free(node2); node2 = NULL; return 0; }
- 编译
- 运行
-
4.创建三个进程,分布使用不同的退出方式进行测试(return, pthread_exit, pthread_cancel)
#include <stdio.h> #include <stdlib.h> #include <pthread.h> int *thread1(void *arg)//return { printf("I am thread1, my tid is %lu\n", pthread_self()); return 111; } void *thread2(void *arg)//pthread { printf("I am thread2, my tid is %lu\n", pthread_self()); pthread_exit((void *)222); } void *thread3(void *arg)//cancel { int i = 0; while(1) { i++; pthread_testcancel(); } return (void *)333; } int main(int args, char *argv[]) { pthread_t tid = 0; void *pointer = NULL; pthread_create(&tid, NULL, (void *)thread1, NULL); pthread_join(tid, &pointer); printf("thread1 return value is %d\n", (int)pointer); pthread_create(&tid, NULL, thread2, NULL); pthread_join(tid, &pointer); printf("thread2 return value is %d\n", (int)pointer); pthread_create(&tid, NULL, thread3, NULL); pthread_cancel(tid); pthread_join(tid, &pointer); printf("thread3 return value is %d\n", (int)pointer); return 0; }
- 编译
-
运行
-
thread增加取消点,线程退出
-
thread3不增加取消点(code_line25 //pthread_testcancel()),线程死锁
-
-
5.设置线程为detach游离态,使用pthread_join判断线程退出状态
#include <stdio.h> #include <unistd.h> #include <pthread.h> void *thread(void *arg) { printf("I am thread, my tid is %lu\n", pthread_self()); pthread_exit((void *)111); } int main(int args, char *argv[]) { pthread_t tid = 0; int error_flag = 0; void *status_pointer = NULL; pthread_create(&tid, NULL, thread, NULL); pthread_detach(tid); error_flag = pthread_join(tid, &status_pointer); printf("thread value is %d\n", (int)status_pointer); printf("error_flag is %d\n", error_flag); return 0; }
- 编译
- 运行
-
line21将线程分离,可见detach后,线程退出后自动回收,其状态无法再用join获得
-
line21注释,保持线程combination,正常获取线程状态111
-