Linux系统编程-线程、互斥锁与多线程模块的封装

Linux系统编程-线程、互斥锁与多线程模块的封装 目录一. 线程1.1 线程概念1.2 线程与进程的区别二. 线程相关函数接口2.1 pthread_create2.2 pthread_exit2.3 pthread_join2.3.1对比记忆2.3.2 注意2.3.3 示例代码2.4 pthread_detach三. 线程的互斥3.1 线程的互斥机制3.2 保护临界资源-互斥锁3.2.1 pthread_mutex_init3.2.2 pthread_mutex_lock/trylock3.2.4 pthread_mutex_unlock(*mutex)3.2.5 pthread_mutex_destroy(*mutex)四. 多线程模块的封装一. 线程1.1 线程概念线程线程是轻量级进程(LWP)1.2 线程与进程的区别进程线程正在执行的程序轻量级进程操作系统资源分配的最小单位操作系统任务调度的最小单位资源空间消耗大0~4G虚拟内存地址空间资源空间消耗小栈区独立其他空间共享进程的效率低线程效率高进程安全性高线程安全性低进程间通信复杂需要用到IPC机制线程间通信方便由于共享数据区使用全局变量即可在相同资源的平台下多进程的并发量要少于多线程的并发量二. 线程相关函数接口2.1 pthread_create作用会在调用进程内启动一个新的线程参数*thread保存新线程的id*attr线程属性*start_routine回调函数线程的执行函数*arg给回调函数的参数返回值成功返回0 失败返回一个错误号2.2 pthread_exit作用pthread_exit()函数会终止调用该函数的线程并通过retval参数返回一个值而这个值则可供同一进程中调用pthread_join()的其他线程使用。当一个线程终止时进程共享资源例如互斥锁、条件变量、信号量和文件描述符不会被释放当进程中的最后一个线程终止后该进程便会像调用exit()并设置退出状态为零那样结束因此进程共享资源会被释放。参数*retval退出值返回值无2.3 pthread_join作用阻塞等待线程退出pthread_join()函数会等待由thread参数指定的线程完成其执行过程。如果该线程已经结束执行那么pthread_join()将立即返回。这个由thread指定的线程必须是非分离状态如果 retval 不为 NULL则 pthread_join() 会将目标线程的退出状态复制到由 retval 所指向的位置。如果目标线程被取消(pthread_cancel)则会在由 retval 所指向的位置放置PTHREAD_CANCELED (-1)标志。参数thread指定线程 **retval保存线程退出状态返回值成功返回0 失败返回一个错误号2.3.1对比记忆线程与进程在接收返回值时的对比进程线程进程在退出时可使用return 0return -1exit(1)exit(0)_exit(0)_Exit(0)等可以看到进程的退出状态都是整型线程的退出可使用return NULLpthread_exit(NULL)等可以看到线程的退出状态都是指针类型另外在pthread_create创建线程时那个回调函数的返回类型就是void*类型这就规定了线程的执行逻辑的返回值类型是void*指针类型在获取进程的退出状态时使用函数wait或者waitpid其中有一个参数int *wstatus来保存进程的退出状态然后使用各种宏来获取进程的退出值等由于进程的退出状态是int故用int*来接收获取线程的退出状态时使用pthread_join第二个参数void **retval就是用来保存线程的退出状态的由于线程的退出状态是void*故使用void**来接收2.3.2 注意注意使用pthread_exit不可返回栈区空间的地址因为线程结束栈区空间释放会出现段错误。以下指针可以通过函数返回全局变量的地址static修饰的局部变量的地址堆区申请的未被释放的空间的地址字符串常量的地址通过函数传参传过来的地址2.3.3 示例代码int num_g 66; void *th_task1(void *arg) { int cnt 5; while(cnt--){ printf(---thread 1 :%ld,pid: %d\n,pthread_self(),getpid()); sleep(1); } //return num_g; return (void*)666; } void *th_task2(void *arg) { while(1){ printf(---thread 2 :%ld, pid : %d\n,pthread_self(), getpid()); sleep(1); } return NULL; } int main() { int ret; void *retval; pthread_t tid1, tid2; ret pthread_create(tid1, NULL,th_task1,NULL); if(ret ! 0){ fprintf(stderr,pthread_create error:%s\n,strerror(ret)); exit(1); } ret pthread_create(tid2, NULL,th_task2,NULL); if(ret ! 0){ fprintf(stderr,pthread_create error:%s\n,strerror(ret)); exit(1); } printf(---thread main :%ld, pid : %d\n,pthread_self(), getpid()); printf(main:%ld,%ld\n,tid1,tid2); pthread_join(tid1, retval); //printf(---pthread join finish:%ld,retval is %d\n,tid1,*(int*)retval); printf(---pthread join finish:%ld,retval is %ld\n,tid1,(long)retval); /这种情况下最好将接收的状态值强转为long型因为64位系统下long与指针都是8位 pthread_join(tid2, NULL); return 0; }结果2.4 pthread_detach作用pthread_detach() 函数将由 thread 标识的线程标记为已分离状态。当一个已分离的线程终止时其资源会自动返回给系统无需其他线程与已终止的线程进行连接。试图分离一个已经分离的线程会导致未定义的行为。参数thread指定的线程返回值成功返回0 失败返回一个错误号分离线程不需要回收的线程当线程结束时可以被操作系统自动回收其资源类似孤儿进程。非分离线程可以被其他回收或者结束的线程默认属性为非分离线程示例代码void *th_task1(void *arg) { return NULL; } int main() { pthread_t tid; int num 0; while(1){ num; printf(num is %d\n,num); pthread_create(tid, NULL,th_task1,NULL); pthread_detach(tid); } return 0; }三. 线程的互斥3.1 线程的互斥机制多线程在访问临界资源时存在资源竞争临界资源多个线程可以同时访问的资源如全局变量共享内存等线程互斥机制让多个线程在访问临界资源时具有排他访问的特性3.2 保护临界资源-互斥锁互斥锁使用流程创建锁-初始化锁-加锁-访问临界资源-解锁-销毁锁3.2.1 pthread_mutex_init功能初始化互斥锁参数互斥锁对象锁的属性返回值成功返回0 失败返回错误码3.2.2 pthread_mutex_lock/trylock功能pthread_mutex_lock以阻塞方式等待锁pthread_mutex_trylock以非阻塞方式等待锁返回值成功返回0 失败返回错误号3.2.4 pthread_mutex_unlock(*mutex)功能解锁返回值成功返回0 失败返回错误号3.2.5 pthread_mutex_destroy(*mutex)功能销毁锁示例代码模拟atm取钱程序pthread_mutex_t mutex; int atm 3; void * pfun(void *arg) { while(1){ //访问atm公共资源先加锁 pthread_mutex_lock(mutex); if(atm 0){ printf(atm %d\n,atm); atm--; printf(--[%ld]:draw money---\n,pthread_self()); pthread_mutex_unlock(mutex);//解锁后取钱 sleep(rand() % 3 1); pthread_mutex_lock(mutex);//加锁再访问atm atm; pthread_mutex_unlock(mutex);//解锁 return NULL; } else{ pthread_mutex_unlock(mutex); } } } int main() { pthread_t tid[10]; int ret; int i 0; srand(time(NULL)); pthread_mutex_init(mutex, NULL); //10个线程去atm取钱 for(i 0; i 10; i){ ret pthread_create(tid[i], NULL, pfun, NULL); if(ret ! 0){ fprintf(stderr,pthread_create error:%s\n,strerror(ret)); exit(1); } } for(i 0; i 10; i){ ret pthread_join(tid[i], NULL); if(ret ! 0){ fprintf(stderr,pthread_join error:%s\n,strerror(ret)); exit(1); } } pthread_mutex_destroy(mutex); printf(main:draw money finish!\n); return 0; }3.3 死锁死锁指的是在多线程环境中每个执行流线程都有未释放的资源且互相请求对方未释放资源从而导致陷入永久等待状态的情况。现象现象1忘记释放锁现象2重复加锁现象3多线程多锁抢占锁资源不当产生死锁的四个必要条件四条全中 死锁破掉任意一条 解除1互斥条件一个资源同一时刻只能被一个任务占用一个执行流获取锁后其它执行流不能再获取该锁。2请求与保持条件已经拿着一部分资源不释放同时又去申请新的资源执行流本身使用着一把锁并不释放还在请求别的锁。3不剥夺条件:资源只能由持有者主动释放系统/其他任务不能强行抢过来A执行流拿着锁其它执行流不能释放。4循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系多个执行流拿着对方想要的锁并且各执行流还去请求对方的锁。解决方法1.锁一定要成对出现2.使线程的加解锁顺序一致3.破坏环路等待条件使用非阻塞锁一旦线程发现请求的锁被使用就去释放自己拥有的资源四. 多线程模块的封装tasks.c/创建多个线程 int creat_thread_tasks(Task_t *tasks, int len) { for(int i 0; i len; i){ int ret pthread_create((tasks[i].tid), NULL, tasks[i].pfun, NULL); if(ret ! 0 ){ fprintf(stderr, pthread_create error:%s\n,strerror(ret)); return -1; } } return 0; } /销毁多个线程 void destory_thread_tasks(Task_t *tasks, int len) { for(int i 0; i len; i){ int ret pthread_join(tasks[i].tid, NULL); } }tasks.h#ifndef __TASKS_H__ #define __TASKS_H__ #includepthread.h typedef struct task{ pthread_t tid; void *(*pfun)(void*); }Task_t; extern int creat_thread_tasks(Task_t *tasks, int len); extern void destory_thread_tasks(Task_t *tasks, int len); #endifmain.c#includestdio.h #include tasks.h #includeunistd.h #includestdlib.h //#includepthread.h void *main_ctl_task(void *arg) { while(1){ printf([%ld]:main_ctl_task is running\n,pthread_self()); sleep(1); } } void *get_usr_cmd_task(void *arg) { while(1){ printf([%ld]:get_usr_cmd_task is running\n,pthread_self()); sleep(1); } } void *exec_usr_cmd_task(void *arg) { while(1){ printf([%ld]:exec_usr_cmd_task is running\n,pthread_self()); sleep(1); } } void * get_pic_task(void *arg) { while(1){ printf([%ld]:get_pic_task is running\n,pthread_self()); sleep(1); } } void *send_pic_task(void *arg) { while(1){ printf([%ld]:send_pic_task is running\n,pthread_self()); sleep(1); } } /Linux支持的部分初始化方式 Task_t tasks[] { { .pfun main_ctl_task, }, { .pfun get_usr_cmd_task, }, { .pfun exec_usr_cmd_task, }, { .pfun get_pic_task, }, { .pfun send_pic_task, }, }; int main() { int ret creat_thread_tasks(tasks,sizeof(tasks)/sizeof(tasks[0])); if(ret -1){ fprintf(stderr, creat_thread_tasks error\n); exit(1); } destory_thread_tasks(tasks,sizeof(tasks)/sizeof(tasks[0])); return 0; }