玩转多线程编程


1 玩转多线程编程 玩转多线程编程 玩转多线程编程 玩转多线程编程 玩转多线程编程 玩转多线程编程 玩转多线程编程 玩转多线程编程 易剑 2012/4/2 2012/4/2 2012/4/2 2012/4/2 致力于开源分布式系统实现:http://code.google.com/p/mooon 2 前言 • 阅读对象 • 适合有一定多线程编程经验的读者 • 所涉及面 • 线程相关的广度 • 线程相关的深度 • 线程相关的设计 • 线程相关的调试 3 线程库 • LinuxThreads • 管理线程 •LWP(1 * 1 模型) •CLONE_VM •NPTL(Native POSIX Thread Library) •CLONE_THREAD • 性能和稳定性 • futex(Fast Userspace muTexes) • int futex (int *uaddr, int op, int val, const struct timespec *timeout,int *uaddr2, int val3); • getconf GNU_LIBPTHREAD_VERSION •LD_ASSUME_KERNEL • 二进制兼容 •-pthread vs -lpthread •-pthread是GCC选项, 相当于“-lpthread D_REENTRANT” •-pthread影响“预处理器”和“链接器” • pthread_exit(0) 4 NPTL vs LinuxThreads •UP:单处理器(Unique Processor) •SMP:对称多处理器(Symmetrical Multi-Processor) 5 线程状态转换图 运行 阻塞 终止 睡眠挂起就绪Resume sleep(N) lock/accept/read CPU Suspend 6 经常遇到的问题 • 死锁(Dead lock) • 程序性能低下 • 对象难以被删除 • 一个对象被线程A删除后,却仍在被线程B使用 • 无规则的使用锁,无清晰的边界 7 多线程编程要点 • 多线程编程难点 • 对象管理 • 多线程编程利器 • 引用计数 • 原子操作 • atomic_read • atomic_set • atomic_add • atomic_sub • atomic_inc • atomic_dec 8 • 应用场景 • 简单、自由、常见 • socket/fd • close(fd)、dup(fd) 多线程编程定式 - Any ObjectObjectObjectObject Thread1Thread1Thread1Thread1 Thread2Thread2Thread2Thread2 * * RefCountableRefCountableRefCountableRefCountable +inc_ref() +dec_ref() 9 • 应用场景 • 网络框架 多线程编程定式 - ST2MO ThreadThreadThreadThread ObjectPoolObjectPoolObjectPoolObjectPool ObjectObjectObjectObject* TimeoutableTimeoutableTimeoutableTimeoutableTimeoutManagerTimeoutManagerTimeoutManagerTimeoutManager ITimeoutHandlerITimeoutHandlerITimeoutHandlerITimeoutHandler +on_timeout_event(Timeoutable*) * 10 多线程编程定式 - SO2MT Thread1Thread1Thread1Thread1 Thread2Thread2Thread2Thread2 ObjectInterfaceObjectInterfaceObjectInterfaceObjectInterface ConcreteObject1ConcreteObject1ConcreteObject1ConcreteObject1 ConcreteObject2ConcreteObject2ConcreteObject2ConcreteObject2 * * DB1DB1DB1DB1 DB2DB2DB2DB2 ObjectManagerObjectManagerObjectManagerObjectManager * ClientClientClientClient • 应用场景 • 异步化/解耦合 11 多线程编程定式 - MT2MO ObjectInterfaceObjectInterfaceObjectInterfaceObjectInterface* +ID+ID ConcreteObject1ConcreteObject1ConcreteObject1ConcreteObject1 ConcreteObject2ConcreteObject2ConcreteObject2ConcreteObject2 RefCountableRefCountableRefCountableRefCountable +inc_ref() +dec_ref() MessageQueueMessageQueueMessageQueueMessageQueue MessageMessageMessageMessage <> +int32_t id * ObjectTableObjectTableObjectTableObjectTable +ObjectInterface* get_object(int32_t id) +bool release_object(ObjectInterface*) Thread1Thread1Thread1Thread1 Thread2Thread2Thread2Thread2 • 应用场景 • 消息调度 12 多线程编程定式 - MT2MO struct Message { uint32_t object_id; }; void WorkThread::run() { while (true) { Message* message = _queue.timed_pop(_timeout_milliseconds); ObjectInterface* object = _object_table->get_object(message->object_id); if (NULL == object) { LOG_INFO("Object %u not exist\n", message->object_id); } else { object->handle(message); _object_table->release(object); } } } 13 多线程编程定式 - MT2MO ObjectInterface* ObjectInterface::get_object(uint32_t object_id) const { ObjectInterface* object = NULL; LockHelper locker(_mutex); iter = _map.find(id); if (iter != _map.end()) { object = iter->second; object->inc_ref(); } return object; } bool ObjectInterface::release_object(ObjectInterface* object) { LockHelper locker(_mutex); uint32_t object_id = object->get_id(); if (object->dec_ref()) { _map.erase(object_id); } } 14 无锁编程(Lock Free) • 原语(Primitive) • 由若干条机器指令构成的完,成某种特定功能的一段程序,具有不可分割性,即原 语的执行必须是连续的,在执行过程中不允许被中断。 •CAS(Compare and Swap) • 三个操作数 • 内存位置(V)、预期原值(A)和新值(B) •ABA问题 •DCAS(Double Compare-And-Swap) •DCL(Double Check Locking) • Hazard Pointers •WRRM(Write-Rarely-Read-Many)Map •“单个写线程/多个读线程”的共享指针 • 同步(Synchronization) • 阻塞型同步(Blocking Synchronization) • 非阻塞型同步(Non-blocking Synchronization) 15 ABA问题 16 阻塞型同步(Blocking Synchronization) • Mutex、Semaphore • 死锁(Deadlock) • 两个迎面走来的人,都不愿给别人让路,都在等对方让路,导致谁也过不 去。 • 活锁(Livelock) • 两个迎面走来的人,靠近的时候需要相互让路,一个人向自己的左边移 动,另外一个人向自己的右边移动这时候就会相撞。 • 饿死(Starvation) •A站着等B开仓放粮,但B一直未去开仓,导致A饿晕了 • 一个线程经常长时间占有某个资源,其他线程就会饿死 • 优先级反转(Priority Inversion) • 共享资源被较低优先级的进程/线程所拥有,较高优先级的进程/线程竞争 该同步资源未获得该资源,而使得较高优先级进程/线程反而推迟被调度执 行的现象。 • 解决办法 • 懒惰法:优先级继承(Priority Inheritance) • 积极法:优先级顶置(Priority Overhead) 17 • threadL和threadH共享资源 优先级反转(Priority Inversion) 18 优先级继承(Priority Inheritance) • threadL继承threadH的优先级 19 优先级顶置(Priority Overhead) • 将threadL的优先级提升为threadH+1 20 非阻塞型同步(Non-blocking Synchronization) • Wait-free • 任意线程的任何操作都可以在有限步之内结束,而不用关心 其它线程的执行速度 • Lock-free • 能确保执行它的所有线程中至少有一个能够继续往下执行 • Obstruction-free • 在任何时间点,一个孤立运行线程的每一个操作可以在有限 步之内结束。只要没有竞争,线程就可以持续运行。一旦共 享数据被修改,Obstruction-free要求中止已经完成的部分操 作,并进行回滚 • 所有Lock-Free的算法都是Obstruction-free的 21 Lock-free应用 • Spinlock(自旋锁) •CAS • 不会产生上下文切换(Context Switching) • pthread_spin_lock • 使用准则:临界区尽量简短,不要有显式或者隐式的系统调用 • Seqlock(顺序锁) • 写优先 • 实现原理 • 依赖一个序列计数器,当写者写入数据时,会得到一把锁,并且将序列值加 1。 当读者读取数据之前和之后,该序列号都会被读取,如果读取的序列号值都相 同,则表明写没有发生。反之,表明发生过写事件,则放弃已进行的操作,重 新循环一次,直至成功。 •RCU(Read-Copy-Update) • 写操作分为写和更新两步,允许读操作在任何时刻无阻塞的运行 •WRRM(Write-Rarely-Read-Many) • 环形队列(Circular Queue) 22 Spinlock vs Mutex 优点 缺点 Spinlock 1.线程状态不会切换 2.一直处于用户态,没有昂贵 的系统调用 1.一直占用CPU 2.会锁住数据总线 Mutex 1.不会死等,得不到锁的时候 会sleep 1.sleep时会陷入内核,发生状态切 换 2.需要昂贵的系统调用 23 环形队列(Circular Queue) • 单个生产者对单个消费者 24 加锁层级 wait-free lock-free obstruction-free atomic lockless-based lock-based 加 锁 粒 度 复 杂 程 度 25 不要失去对线程的控制权 • 原则 • 尽量避免调用sleep,实在无法避免也应当让sleep的时长尽可能的 小 • 避免以阻塞且无超时方式调用accept/read等 • 危害 • 难以实施死锁检测 • 难以实现优雅退出 • 建议 • sleep时长控制在10秒以内 • 使用可唤醒的替代sleep,如pthread_cond_timedwait替代sleep • 以超时或非阻塞方式使用accept/read等 • 切记 • 牢牢把握对线程的控制权 26 死锁(Deadlock) • 产生原因 • 嵌套锁调用 • 锁中嵌套阻塞式调用,如accept置于锁保护中 • 死锁检测 • 不要失去对线程的控制权 void ClassA::foo() { LockHelper locker(_mutex); // Do something here } void ClassB::woo() { LockHelper locker(_mutex); _a->foo(); } 27 优化锁使用 • 减少锁碰撞时长 • 最影响性能 • 现象之一:CPU压不上来 • 减少锁作用粒度 • 能减少和降低锁碰撞时长 • 减少锁调用次数 • 尽量使用原子锁 • 整数的加减和比较等 • 无锁化 28 GDB • info threads • thread • thread apply • thread apply all command • set scheduler-locking off|on|step • off:不锁定任何线程,所有线程都可以执行 • on:只有当前被调试的线程会执行 • step:在单步的时候,除了next过一个函数的情况以外,只有当前线程会执行 • maintrance • maintrance translate
29 GDB • x /nfu
• x是examine的缩写 • 用途:查看address处的内容 • n表示要显示的内存单元的个数 • f表示显示方式, 可取值: • x 按十六进制格式显示 • d 按十进制格式显示 • u 按十进制格式显示 • o 按八进制格式显示 • t 按二进制格式显示 • a 按十六进制格式显示 • i 指令地址格式 • c 按字符格式显示 • f 按浮点数格式显示 • s 按字符串格式显示 • u表示一个地址单元的长度,可取值: • b表示单字节 • h表示双字节 • w表示四字节 • g表示八字节 30 编译 •-rdynamic • 通知链接器将所有符号添加到动态符号表中 • strip命令 •-fPIE • 生成与位置无关的可执行代码 •-fPIC • 生成与位置无关的非可执行代码 •-Wl,-e,foo • 指定入口函数,相当于可执行程序的main函数 • http://bbs.hadoopor.com/thread-3313-1-1.html 31 编译优化 • 编译器优化 • 将内存变量缓存到寄存器 • 调整指令顺序,充分利用CPU指令流水线 • volatile vs const • Register vs RAM • 提醒编译器不要对变量进行优化 • 内存屏障(Memory Barrier) • 对内存的写入操作不能及时地反映出来 32 内存屏障 • 乱序执行(取指、译码、访存、执行。。。) • void barrier() •#define barrier() __asm__ __volatile__("": : :"memory"); • 只约束GCC编译器,不约束运行时的CPU行为 33 •#include 内存屏障宏 宏定义 功能说明 mb() 适用于多处理器和单处理器的内存屏障 rmb() 适用于多处理器和单处理器的读内存屏障 wmb() 适用于多处理器和单处理器的写内存屏障 smp_mb() 适用于多处理器的内存屏障 smp_rmb() 适用于多处理器的读内存屏障 smp_wmb() 适用于多处理器的写内存屏障 34 其它 • 单个进程最大线程数 •/proc/sys/kernel/threads-max • 线程栈 • pthread_attr_getstacksize,pthread_attr_setstacksize • 设置线程名 • 进程title •/proc/$pid/cmdline • argv[0] • ps aux • 进程name •/proc/$pid/stat • prctl(PR_SET_NAME, thread_name); • top,killall,ps -e -o pid= -o comm= • 调用栈 • int backtrace (void **addresses, int max_depth); • char** backtrace_symbols (void *const *addresses, int real_depth); 35 20122012201220122012201220122012OverOverOverOverOverOverOverOver!!
还剩34页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

lizg2010

贡献于2012-09-03

下载需要 15 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf