版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、多线程编程实践,贺鸿富 (),内容,多线程安全编程基础,1,1,多线程调试排错实践,3,线程安全编写实践,2,多线程安全编程基础,多线程用途,通过并发能力提高处理性能 分离执行路径,由阻塞式路径变成异步路径 主动处理事件的独立模块 遵循框架和程序管理规则,线程安全,模块的线程安全:在多线程环境下模块可正确运行 全局资源的线程安全:某全局变量在多线程环境下正确访问 int g_value; gvalueLock.lock (); g_value = 10; gvalueLock.unlock (); 类对象的线程安全: XXXThread:run (void) obj-foo (); ,互斥与同
2、步,互斥:多个线程同时访问某一资源时,确保此资源某一时刻只有一个线程访问 gvalueLock.lock (); g_value = 10; gvalueLock.unlock (); 同步:多个线程并发处理时,保持每一个线程按状态同步,如在生产者消费者模型中,生产者线程完成第一个生产后,通知消费者等待线程开始消费 _objects.push_back (obj); if (_object.size () = 0) if (_objects.size () = 1) _event.wait (); _event.signal ();,多线程编程问题,访问冲突: _vec.push_back (
3、obj); 此操作具有大量的子代码:分配空间、多个成员的赋值等 死琐: 等待条件永不达成:if (xxx) obj.wait (); 互相琐住,Foo () _a.lock (); _b.lock (); ,Xoo () _b.lock (); _a.lock (); ,Foo () _a.lock (); Xoo (); Xoo () _b.lock (); Foo (); ,CFL 琐类型,在当前版本的 CFL 中,主要包括提供了两种类型的琐: ThreadMutexLock: 采用 CritialSection 实现,用于线程间互斥的琐。 ProcessMutex:用于实现进程间的互斥琐
4、。 另外: EventLock: 采用 CreateEvent 机制实现的琐,暂不推荐使用。 MutexLock: 采用 CreateMutex 机制实现的琐,暂不推荐使用。,ACE 同步事件类型,跨平台的事件同步机制 基于 Windows Event 事件机制,包括: 具有“信号态”和“无信号态”两种状态。当某一线程等待一个事件时,如果事件为信号态,将继续执行,如果事件为无信号态,那么线程被阻塞。 信号态时,可触发正在等待的线程 一般都使用 ACE_Auto_Event,自动事件对象,可自动恢复信号状态,类型应用场景,1. ThreadMutexLock 是线程间的互斥琐,它是非内核对象,所
5、以性能是琐类型中最好的。 2. 线程同步尽量使用 ACE_Auto_Event 类型 3. ACE_Manual_Event 类型 需要调用 reset 恢复到“无信号态” 4. EventLock 琐即使是在同一线程,也会阻止,而其它类型的琐,在同一线程是 不会互斥的,例如: EventLock lock; lock.lock (); lock.lock (); / 将在此产生死琐! 5. 在 POSIX pthread 库中,pthread_mutex_t 的琐机制,调用 pthread_mutex_lock 时,产生的效果跟 CreateEvent 中调用的lock 是一样,这 就会导致
6、在同一线程中连续调用 pthread_mutex_lock(),第二次就会死琐。,线程安全编写实践,多线程编程注意事项,不要强制停止线程 要使用琐保护共享资源 对于非共享资源,不要使用琐保护 多线程下单个整数值的安全访问 同步事件的通知与等待调用次数需要匹配 保证琐的粒度最小 尽量设计lockless的类和模块,不要强制停止线程,class abTestThread : public Thread public: void run (void) while (true) foo (); void foo (void) String fileName (_T(C:boot.ini); File
7、file (fileName); FileInputStream out (file, NONE_SHARE); / . ; 强制停止,不会进行堆栈清理,在 foo() 中可能泄漏的资源包括打开的文件句柄、分配的堆内存。,琐与共享资源的关系,琐与共享资源的关系应该是一对一的关系: 确定共享资源的范围(可能是一个成员,也可能是多个成员) 每一个共享资源应该使用同一把琐 非共享资源不需要使用琐保护,错误示例一: class A A (Lock* lock) : _lock (lock) void foo () _lock-lock (); _value = 10; _lock-unlock();
8、,错误示例二: class A void foo () _lock-lock (); _value = 10; _yield = 20; _lock-unlock(); void xoo () _lock-lock (); _value = 10; _lock-unlock(); _yield = 20; ,错误示例三: class A void foo () _lock-lock (); _value = 10; _yield = 20; _lock-unlock(); void xoo () _lock-lock (); int a = 1000; _value = 10; _lock-u
9、nlock(); _yield = 20; ,多线程下单整数的安全访问,通常有一个整数值需要读和写,直接读写并非最安全代码,需要使用InterlockedExchange(),此方法虽为Windows接口,但已经移植到所有平台上,Int ncFOIPReaderManager:getStatus (void) int status = NORMAL_STATUS; :InterlockedExchange (TO_ATOMIC( ,同步事件的引用计数规则,同步事件的通知与等待调用次数需要匹配: 每一次 notify 都会增加一次内部计数 每一次 wait 都会检查一次内部计数,如果非零,则会不
10、等待,并且减少一次内部计数 ACE_Auto_Event 的notify/wait是基于计数机制的 同步事件的通知与等待调用次数需要匹配,void A:produce (void) _objects.push_back (obj); if (_objects.size () 0) _notifier.notify (); ,void A:consume (void) if (_objects.empty () = true) _notifier.wait (); ,多线程的并发性能,启用多线程的目的之一是通过并发提高性能,理想的并发路线是:,线程2,线程1,线程3,多线程的并发性能,现实中,由
11、于琐用于保护共享资源不够慎重,将产生很多原子性的长调用,而非短调用: 原子性长调用:此调用的入口具有互斥琐,并且该琐将互斥后续一系列原子调用 原子性短调用:此调用的互斥琐仅互斥本调用里的共享资源,不互斥子调用的共享资源,线程2,线程1,因此:琐的粒度一定要越小越好,否则就会显著降低多线程的并发性能!,因长调用,多线程成线性调用!,细粒度琐的关键问题,如何设计琐粒度? 如何防止交叉锁产生死琐? 如何保证过程的一致性?,设计琐的粒度,根据调用层次设计 如:全局琐(_rootLock)、对象琐(ncObjectNode-lock)、特定用途琐(_neighborLock),根据待保护的共享资源设计:
12、 A 用于保护数据成员X,Y,Z,B 用于保护数据成员 U,V,W,根据用途来设计 如ncObjectNode有三个琐,对象属性琐、读琐、写琐,防止交叉琐,交叉锁,或交叉事件同步,必然会导致死琐,这也是多线程编程时产生死琐的主因(高达90%),其判断方法包括: 当前调用细粒度琐是否琐定了子调用 子调用是否有内部琐? 如果有内部琐,是否琐定了第三方对象的调用 第三方对象(可能是本对象)是否与当前调用有代码互斥,并且访问的代码在琐定范围? 即形成了一个琐闭合,其防止方法为打破闭合,缩小琐的范围,调用A,调用B,调用B,调用C,调用A,调用B,调用C,调用A,保证过程的一致性,何为过程的一致性?例如
13、,在多线程环境下调用: TestThread:run (void) printf (Hello); printf (World); printf (n); 启用多个线程运行后,并不是按每行 “HelloWorld”来输出,这是因为printf是原子调用,但每一个线程在不断交叉调用。 简单的处理方法是将过程采用琐保护起来,形成一个长调用,但会显著影响性能 在细粒度琐的情况,尤其要注意此问题,其避免方法只能是算法级和逻辑级的,它须根据实际来调整代码逻辑和算法逻辑,设计 lockless 的模块,创建对象,访问索引树,保存索引节点,写数据,访问索引树,保存数据块,保存索引节点,由一个线程来统一处理
14、结果和参数都由中间队列传递 只有中间队列为共享资源 lockless 的类也可以采用类似的方法,多线程调试排错,多线程调试的难点,难以定位代码故障位置 断点调试,容易破坏执行路径,导致问题难以单步跟踪 因为无法单步跟踪,无法获得现场发生前的条件,免外围干扰排错法断言,使用 AB_ASSERT() 断言,将发生错误的条件提前 ncDataBlockIndex* blockIndex (new ncDataBlockIndex); blockIndex-offset= table-offset; blockIndex-blockLength= table-length; blockIndex-pa
15、dding= table-padding; AB_ASSERT (blockIndex-offset offset = 200) int i = 0; +i; / 在此设置一个断点,则可以在此条件下继续执行 ,免外围干扰排错法断点条件,在前面一种断点调试方法中,需要不断修改代码,编译代码以便定位,因此常常会把现场破坏,在 Visual Studio 开发环境中,可以使用调试工具的断点条件来进行定位跟踪: 设置断点条件:,pstack 工具 for linux,pstack是SunOS上的一个很有用的工具,可以查看core dump文件,调试进程等等。 现在,pstack工具也移植到了Linux
16、系统中,比如Red Hat Linux系统、Ubuntu Linux系统等等。 可使用pstack检测线程间死锁,如某程序发生了死琐,则可使用gstack查看堆栈: pstack pgrep deadlock_test pstack.log,pstack 工具 for linux,Thread 3 (Thread -1208841328 (LWP 2594): #0 0 x0067b402 in _kernel_vsyscall () #1 0 x00a0baee in _lll_mutex_lock_wait () #2 0 x00a078e0 in _L_mutex_lock_85 ()
17、from /lib/i686/nosegneg/libpthread.so.0 #3 0 x00a0742d in pthread_mutex_lock () from /lib/i686/nosegneg/libpthread.so.0 #4 0 x0804912d in _threadWrite () #5 0 x00a05462 in start_thread () from /lib/i686/nosegneg/libpthread.so.0 #6 0 x0095c2ce in clone () from /lib/i686/nosegneg/libc.so.6 Thread 2 (T
18、hread -1219331184 (LWP 2595): #0 0 x0067b402 in _kernel_vsyscall () #1 0 x00a0baee in _lll_mutex_lock_wait () #2 0 x00a078e0 in _L_mutex_lock_85 () from /lib/i686/nosegneg/libpthread.so.0 #3 0 x00a0742d in pthread_mutex_lock () from /lib/i686/nosegneg/libpthread.so.0 #4 0 x08048ccb in _threadRead ()
19、 #5 0 x00a05462 in start_thread () from /lib/i686/nosegneg/libpthread.so.0 #6 0 x0095c2ce in clone () from /lib/i686/nosegneg/libc.so.6 Thread 1 (Thread -1208838448 (LWP 2593): #0 0 x0067b402 in _kernel_vsyscall () #1 0 x00a06587 in pthread_join () from /lib/i686/nosegneg/libpthread.so.0 #2 0 x08048
20、c7e in main () #0 0 x0067b402 in _kernel_vsyscall (),pstack 工具 for linux,从stack的信息可以看出,Thread 3(LWP 2594)和Thread 2(LWP 2595)都处于申请 锁的状态,但是这些锁不是立即可得的,它们都在等待其它线程释放它们各自所需 的锁。它们的等待状态无法结束,显然有其它线程占用了它们所申请的资源。因为 Example 1比较简单,线程数较少,我们可以看到处于等待状态的线程就只有 Thread 2和Thread 3,必然是它们之间产生了死锁。Thread 2调用的是 _threadRead()
21、方法,Thread 3调用的是_threadWrite()方法,结合代码,可以很 容易看出Thread 2占用了线程锁score_mutex而去申请线程锁info_mutex,Thread 3则占用了线程锁info_mutex而去申请线程锁score_mutex。它们都在等待对方释放 自己需要的锁,然后再释放自己占用的锁,于是出现了死等待现象,也就是死锁。 这是交叉琐产生死琐的示例,未知错误的代码定位,排除法:通过排除可能出错的代码,诊断出错误发生的范围 围栏法:通过不断缩小围栏,精确定位错误发生的代码位置 Step 法:可在非调试环境中跟踪错误发生的精确位置和原因,未知错误的代码定位排除法,困境:在多线程程序运行中出现意外崩溃,无堆栈信息,难以预计出现故障的位置,而且出现的问题很诡异,无法
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年广州市星海音乐学院引进高层次人才备考题库及参考答案详解
- 西方古代官员责任制度
- 中空玻璃岗位责任制度
- 家长委员各种责任制度
- 学校食堂责任制管理制度
- 燃气间安全生产责任制度
- 泵站安全生产责任制度
- 科技工作者岗位责任制度
- 创卫工作岗位责任制度
- 水库安全责任制度范本
- 学生5mm坐标纸(虚线-文本版)直接打印
- 小班语言:迎春花
- 别克君威4T65E自动变速器
- 2024年江苏农林职业技术学院高职单招(英语/数学/语文)笔试历年参考题库含答案解析
- 建设工程项目经济分析与评价PPT完整全套教学课件
- 技术交底制度
- 废塑料高温裂解干馏可行性报告
- 地质勘探原始记录表格【实用文档】doc
- GB/T 30812-2014燃煤电厂用玻璃纤维增强塑料烟道
- 住院医师规范化培训临床技能结业考核体格检查评分表(神经外科)
- 小学二年级下册体育教案(全册)
评论
0/150
提交评论