/*Linux环境编程:从应用到内核*/ /* 0.4.3 线程安全 线程安全,顾名思义是指代码可以在多线程环境下“安全”地执行。何为安全?即符合正确的逻辑结果,是程序员 期望的正常执行结果。为了实现线程安全,该代码要么只能使用局部变量或资源,要么就是利用锁等同步机制,来 实现全局变量或资源的串行访问。 6.1信号的完整声明周期 信号的本质是一种进程间的通信。进程之间约定好:如果发生了某件事情T(trigger),就向目标进程(destination process)发送某特定信号X,而目标进程看到X,就意识到T事件发生了,目标进程就会执行相应的动作A(action)。 总结:Linux的signal机制是一种原始的进程间通信机制,传递信息有限,很难传递复杂的消息,加上信号处理函数和 进程处于两条执行逻辑流,会带来函数的重入问题,因此signal机制不适合作为进程间通信的主要手段。但是 信号又不是完全无用的,对于某些不频繁发生的异步事件,还是可以使用signal来通知进程。 */ /* 7 理解linux线程 设计多线程的原因: 发挥多核优势,充分利用CPU资源 更自然的编程模型:很多程序,天生就适合用多线程。将工作切分成多个模块,并为每个模块分配一个或多个 执行单元,更符合人类解决问题的思路。 多线程存在的弊端: 多个线程之中,只要有一个线程不够健壮存在bug(如访问了非法地址引发的段错误),就会导致进程内的所有 线程一起完蛋。 线程模型作为一种并发的编程模型,效率并没有想象的那么高,会出现复杂度高、易出错、难以测试和定位问题。 多线程之间存在负载均衡的问题; 多个线程之间需要同步:若多个线程操作共享资源,则需要同步;因此多线程编程中存在临界区的概念,临界区 的代码只允许一个线程执行,线程提供了锁机制来保护临界区。当其他线程来到临界区却无法申请到锁时,就可能 陷入阻塞,不再处于可执行状态。 有人曾经这样打比方:“多进程属于立体交通系统,虽然造价高,上坡下坡比较耗油,但是堵车少;多线程属于平面 交通系统,造价低,但是红绿灯太多,老堵车”。 表7-4 POSIX线程库的接口 POSIX函数 函数功能描述 pthread_create 创建一个线程 pthread_exit 退出线程 pthread_self 获取线程ID pthread_equal 检查两个线程ID是否相等 pthread_join 等待线程退出 pthread_detach 设置线程状态为分离状态 pthread_cancel 线程的取消 pthread_cleanup_push/pthread_cleanup_pop 线程退出,清理函数注册和执行 线程创建销毁的流程 Main Thraed->pthread_ceate()->Thread->pthread_exit()->pthread_join()->exit() 不连接(pthread_join)已经退出的线程,会导致资源无法释放。 互斥量 为什么需要互斥量? 大部分情况下,线程使用的数据都是局部变量,变量的地址在线程栈空间内,这种情况下, 变量归属于单个线程,其他线程无法获取这种变量。 但实际情况是,很多变量都是多个线程共享的,这样的变量称为共享变量。可通过数据的共享,完成多个线程之间 的交互。 */ //多线程及互斥锁代码 #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <string.h> #include <errno.h> #include <sys/types.h> #define LOOP_TIMES 10000000 #define NR_THREAD 4 pthread_rwlock_t rwlock; pthread_mutex_t mutex; int global_cnt = 0; //共享变量 void* thread_work(void* param) { int i; pthread_rwlock_rdlock(&rwlock); for(i=0; i < LOOP_TIMES; i++) { pthread_mutex_lock(&mutex); //互斥量加锁 global_cnt++; pthread_mutex_unlock(&mutex); //互斥量关锁 } pthread_rwlock_unlock(&rwlock); return NULL; } int main(int argc, char* argv[]) { pthread_t tid[NR_THREAD]; char err_buf[1024]; int i, ret; ret=pthread_rwlock_init(&rwlock, NULL); //读写锁 if(ret) { fprintf(stderr, "init rw lock failed (%s) ", strerror_r(ret, err_buf, sizeof(err_buf))); exit(1); } ret=pthread_mutex_init(&mutex, NULL); if(ret) { fprintf(stderr, "init mutex lock failed (%s) ", strerror_r(ret, err_buf, sizeof(err_buf))); exit(1); } pthread_rwlock_wrlock(&rwlock); for(i=0; i<NR_THREAD; i++) { ret = pthread_create(&tid[i], NULL, thread_work, NULL); if(ret != 0) { fprintf(stderr, "create thread failed, return %s ", strerror_r(ret, err_buf, sizeof(err_buf))); exit(1); } } pthread_rwlock_unlock(&rwlock); //子线程创建完成,主线程的读写锁关闭,4个线程同时启动 for(i=0; i<NR_THREAD; i++) { pthread_join(tid[i], NULL); //等待所有线程任务完成; } pthread_mutex_destroy(&mutex); pthread_rwlock_destroy(&rwlock); printf("threadnum: %d ",NR_THREAD); printf("loops per thrad: %d ",LOOP_TIMES); printf("expect result: %d ", LOOP_TIMES*NR_THREAD); printf("actual result: %d ", global_cnt); exit(0); } /* 注意事项: 1.编译方法:gcc mutex.c -o mutex -lpthread pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a 所以在使用pthread_create()创建线程,及pthread_rwlock*读写锁,处理程序时,需要链接该库 2.互斥量和读写锁使用完成后都要手动销毁 */ /* 7.8读写锁 很多时候,对共享变量的访问有以下特点:大多数情况下线程只是读取共享变量的值,并不修改,只有极少数情况下,线程才会真正的修改共享变量的值。 对于这种情况,读请求之间是无需同步的,它们之间的并发访问是安全的。然而写请求必须锁住读请求和其他请求。 这种情况在实际中是存在的,比如配置项。大多数时间内,配置是不会发生变化的,偶尔会出现修改配置的情况。如果使用互斥量,完全阻止请求并发,则会 造成性能损失。 */