《UNIX环境高级编程》第11章线程【读书笔记】
《UNIX环境高级编程》第11章线程【读书笔记】
- 参数tidp:指向的内存单元被设置为新创建线程的线程ID。
- 参数attr:新创建线程的属性。
- 参数start_rtn:新创建线程从这个函数的地址开始运行。
- 参数arg:函数start_rtn的参数地址(可多参数打包为结构体)。
相关阅读:
《UNIX环境高级编程》(第二版)apue.h的错误
UNIX环境高级编程中文第二版PDF高清版 http://www.bkjia.net/thread-2063-1-1.html
- 线程从启动例程中返回,返回值是线程的退出码。
- 线程可以被同一进程中的其他线程取消。
- 线程调用pthread_exit。
- pthread_join(pthread_t thread, void **rval_ptr)用于阻塞调用进程,直到指定的线程从以上三种退出方式中的一种返回。此函数的第1个参数thread表示要等待的线程ID,第2个参数包含返回码,如果对线程的返回值并不感兴趣,则可以将rval_ptr设为NULL。
- 进程中的其它线程可以通过调用pthread_join函数来获得某线程的退出状态,rval_ptr也可以传递包含更复杂信息的结构的地址,但是注意这个结构所使用的内存在调用者完成调用以后必须仍然是有效的,否则就会出现无效或非法内存访问。比如,线程在自己的栈上分配了一个结构,然后把指向这个结构的指针传给pthread_exit,那么调用pthread_join来等待此线程的调用线程试图使用该结构时,这个栈有可能已经被撤销(被等待的线程已经从启动例程返回了,栈已收回),这块内存也已另作他用(访问得到的结构值无意义了)。为了解决这个问题,可以使用全局结构或者用malloc函数分配结构。
- 线程可以通过调用pthread_cancel(pthread_t pid)函数来请求取消同一进程中的其他线程,此函数并不等待线程终止,它仅仅提出请求。
- 线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序(thread cleanup handler)。线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说它们的执行序列与注册的顺序相反。通过调用pthread_cleanup_push(void(*rtn)(void *), void *arg)注册。而当程序执行以下动作时会调用清理函数:
- 调用pthread_exit时。
- 响应取消请求时。
- 用非零execute参数调用pthread_cleanup_pop(int execute)。如果execute置为0,清理函数将不被调用,但会出栈删除。
- 如果线程是通过从它的启动例程中返回而终止的话,那么它的清理处理程序就不会被调用。
- 如果线程已经处于分离(detach)状态,则线程的底层存储资源可以在线程执行时(被系统自动)立即收回。当线程被分离时,并不能用pthread_join来等待它的终止状态。对分离状态的线程调用pthread_join会产生失败,返回EINVAL。pthread_detach(pthread_t tid)调用可以使线程处于分离状态,也可以在pthread_create创建线程时通过指定attr来完成。
- 当多个控制线程共享相同的内存时,需要确保每个线程看到一致的数据视图。当某个线程可以修改变量,而其他线程也可以读取或者修改这个变量时,就需要对这些线程进行同步,以确保它们在访问变量的存储内容时不会访问到无效的数值。
- 如果修改是原子操作,那么就不存在竞争。但是在现代计算机系统中,存储器访问需要多个总线周期,多处理器的总线周期通常在多个处理器上是交叉的,所以无法保证数据是顺序一致的。
- 互斥量(mutex)用pthread_mutex_t数据类型表示。使用之前需要初始化,可将其置为常量 PTHREAD_MUTEX_INITIALIZER(只对静态互斥量),也可通过pthread_mutex_init函数进行初始化。如果动态地分配互斥量,则在释放内存之前 需要调用pthread_mutex_destroy。
- 对互斥量加锁,需要调用pthread_mutex_lock,若互斥量已上锁,则调用线程阻塞。可调用pthread_mutex_trylock尝试加锁,若加锁失败,不会阻塞而是返回EBUSY。调用pthread_mutex_unlock解锁。
- 如果释放互斥锁时有多个线程阻塞,则这些线程均会变成可运行状态,第一个变为运行状态的线程可以对互斥量加锁,其它线程只能继续等待。
- 只有在一个线程试图以与另一个线程相反的顺序锁住互斥量时,才可能出现死锁。
- 当同时需要用两个互斥量时,总是让它们以相同的顺序加锁,以避免死锁。
- 如果锁的粒度太粗,就会出现很多线程阻塞等待相同的锁,源自并发性的改善微乎其微。如果锁的粒度太细,那么过多的锁开销会使系统性能受到影响,而且代码会变得相当复杂。作为一个程序员,需要在满足锁需求的情况下,在代码复杂性和优化性能之间找到平衡点。
- 虽然读写锁的实现各不相同,但当读写锁处于读模式锁住状态时,如果有另外的线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁请求,防止读模式锁长期占用,而等待的写模式锁请求一直得不到满足。
- 读写锁非常适合于对数据结构读的次数远大于写的情况。
- 读写锁相关调用函数:初始化 pthread_rwlock_init(rwlock,arr); 销毁锁pthread_rwlock_destroy(rwlock); 读模式锁定 pthread_rwlock_rdlock(rwlock); 写模式锁定 pthread_rwlock_wrlock(rwlock); 解锁 pthread_rwlock_unlock(); 读模式试锁定 pthread_rwlock_tryrdlock(rwlock); 写模式试锁定 pthread_rwlock_trywrlock(rwlock);
- 要在释放读写锁占用的内存之前调用destroy函数做清理工作,否则如果调用之前就释放了锁占用内存,则分配给这个锁的资源就丢失了(无法destroy了)。
- 条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
- 初始化 pthread_cond_init(cond, attr); 销毁 pthread_cond_destroy(cond);等待条件变为真 pthread_cond_wait(cond,mutex); pthread_cond_timedwait(cond,mutex,timeout);后者指定了等待的时间。通知线程条件已经满足pthread_cond_signal(cond)和pthread_cond_broadcast(cond);前者是唤醒等待该条件的某线程,后者唤醒所有等待线程。
评论暂时关闭