多线程编程课件

上传人:阳*** 文档编号:84067148 上传时间:2022-05-02 格式:PPTX 页数:34 大小:653.78KB
收藏 版权申诉 举报 下载
多线程编程课件_第1页
第1页 / 共34页
多线程编程课件_第2页
第2页 / 共34页
多线程编程课件_第3页
第3页 / 共34页
资源描述:

《多线程编程课件》由会员分享,可在线阅读,更多相关《多线程编程课件(34页珍藏版)》请在装配图网上搜索。

1、多线程编程实践贺鸿富 (he.)多线程编程内容多线程安全编程基础11多线程调试排错实践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 ();多线程编程 多线程编程问题 访问冲突: _ve

3、c.push_back (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 (_T(C:boot.ini); (

7、); out (file, NONE_SHARE); / . ; 强制停止,不会进行堆栈清理,在 foo() 中可能泄漏的资源包括打开的文件句柄、分配的堆内存。多线程编程 琐与共享资源的关系琐与共享资源的关系应该是一对一的关系: 确定共享资源的范围(可能是一个成员,也可能是多个成员) 每一个共享资源应该使用同一把琐 非共享资源不需要使用琐保护错误示例一:class AA (Lock* lock) : _lock (lock)void foo () _lock-lock (); _value = 10; _lock-unlock();错误示例二:class A void foo () _lock

8、-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-unlock(); _yield = 20;多线程编程 多线程下单整数的安全访

9、问通常有一个整数值需要读和写,直接读写并非最安全代码,需要使用InterlockedExchange(),此方法虽为Windows接口,但已经移植到所有平台上Int ncFOIPReaderManager:getStatus (void) int status = NORMAL_STATUS; :InterlockedExchange (TO_ATOMIC(&status), _status); return status;void ncFOIPReaderManager:stopRead (void) if (getStatus () = NORMAL_STATUS) / _status =

10、 NORMAL_STATUS :InterlockedExchange (TO_ATOMIC(&_status), STOPPED_STATUS); _readerEvent.signal (); 多线程编程 同步事件的引用计数规则 同步事件的通知与等待调用次数需要匹配: 每一次 notify 都会增加一次内部计数 每一次 wait 都会检查一次内部计数,如果非零,则会不等待,并且减少一次内部计数 ACE_Auto_Event 的notify/wait是基于计数机制的同步事件的通知与等待调用次数需要匹配void A:produce (void) _objects.push_back (obj)

11、; if (_objects.size () 0) _notifier.notify (); void A:consume (void) if (_objects.empty () = true) _notifier.wait ();多线程编程 多线程的并发性能启用多线程的目的之一是通过并发提高性能,理想的并发路线是:原子调用1原子调用2原子调用3原子调用4线程2线程1原子调用1原子调用2原子调用3原子调用4线程3原子调用1原子调用2原子调用3原子调用4多线程编程 多线程的并发性能现实中,由于琐用于保护共享资源不够慎重,将产生很多原子性的长调用,而非短调用: 原子性长调用:此调用的入口具有互斥

12、琐,并且该琐将互斥后续一系列原子调用 原子性短调用:此调用的互斥琐仅互斥本调用里的共享资源,不互斥子调用的共享资源原子短调用1原子长调用2原子短调用3原子短调用4线程2线程1原子短调用1原子长调用2原子短调用3原子短调用4因此:琐的粒度一定要越小越好,否则就会显著降低多线程的并发性能!因长调用,多线程成线性调用!多线程编程 细粒度琐的关键问题 如何设计琐粒度? 如何防止交叉锁产生死琐? 如何保证过程的一致性?多线程编程 设计琐的粒度 根据调用层次设计如:全局琐(_rootLock)、对象琐(ncObjectNode-lock)、特定用途琐(_neighborLock) 根据待保护的共享资源设计

13、: A 用于保护数据成员X,Y,Z,B 用于保护数据成员 U,V,W根琐一类琐二类琐二类琐一类琐根据用途来设计 如ncObjectNode有三个琐,对象属性琐、读琐、写琐多线程编程 防止交叉琐交叉锁,或交叉事件同步,必然会导致死琐,这也是多线程编程时产生死琐的主因(高达90%),其判断方法包括: 当前调用细粒度琐是否琐定了子调用 子调用是否有内部琐? 如果有内部琐,是否琐定了第三方对象的调用 第三方对象(可能是本对象)是否与当前调用有代码互斥,并且访问的代码在琐定范围?即形成了一个琐闭合,其防止方法为打破闭合,缩小琐的范围调用A调用B调用B调用C调用A调用B调用C调用A多线程编程 保证过程的一

14、致性何为过程的一致性?例如,在多线程环境下调用: TestThread:run (void) printf (Hello); printf (World); printf (n); 启用多个线程运行后,并不是按每行 “HelloWorld”来输出,这是因为printf是原子调用,但每一个线程在不断交叉调用。 简单的处理方法是将过程采用琐保护起来,形成一个长调用,但会显著影响性能 在细粒度琐的情况,尤其要注意此问题,其避免方法只能是算法级和逻辑级的,它须根据实际来调整代码逻辑和算法逻辑多线程编程 设计 lockless 的模块创建对象访问索引树保存索引节点写数据访问索引树保存数据块保存索引节点

15、由一个线程来统一处理 结果和参数都由中间队列传递 只有中间队列为共享资源 lockless 的类也可以采用类似的方法多线程编程多线程调试排错多线程编程 多线程调试的难点 难以定位代码故障位置 断点调试,容易破坏执行路径,导致问题难以单步跟踪 因为无法单步跟踪,无法获得现场发生前的条件多线程编程 免外围干扰排错法断言使用 AB_ASSERT() 断言,将发生错误的条件提前 ncDataBlockIndex* blockIndex (new ncDataBlockIndex); blockIndex-offset= table-offset; blockIndex-blockLength= tab

16、le-length; blockIndex-padding= table-padding; AB_ASSERT (blockIndex-offset offset pstack.log多线程编程 pstack 工具 for linuxThread 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 () from /lib/i686/nosegneg/lib

17、pthread.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.6Thread 2 (Thread -1219331184 (LWP 2595):#0

18、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 ()#5 0 x00a05462 in start_thread () fro

19、m /lib/i686/nosegneg/libpthread.so.0#6 0 x0095c2ce in clone () from /lib/i686/nosegneg/libc.so.6Thread 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 x08048c7e in main ()#0 0 x0067b402 in _kernel_vsy

20、scall ()多线程编程 pstack 工具 for linux从stack的信息可以看出,Thread 3(LWP 2594)和Thread 2(LWP 2595)都处于申请锁的状态,但是这些锁不是立即可得的,它们都在等待其它线程释放它们各自所需的锁。它们的等待状态无法结束,显然有其它线程占用了它们所申请的资源。因为Example 1比较简单,线程数较少,我们可以看到处于等待状态的线程就只有Thread 2和Thread 3,必然是它们之间产生了死锁。Thread 2调用的是_threadRead()方法,Thread 3调用的是_threadWrite()方法,结合代码,可以很容易看出T

21、hread 2占用了线程锁score_mutex而去申请线程锁info_mutex,Thread3则占用了线程锁info_mutex而去申请线程锁score_mutex。它们都在等待对方释放自己需要的锁,然后再释放自己占用的锁,于是出现了死等待现象,也就是死锁。这是交叉琐产生死琐的示例这是交叉琐产生死琐的示例多线程编程 未知错误的代码定位 排除法:通过排除可能出错的代码,诊断出错误发生的范围 围栏法:通过不断缩小围栏,精确定位错误发生的代码位置 Step 法:可在非调试环境中跟踪错误发生的精确位置和原因多线程编程 未知错误的代码定位排除法困境:在多线程程序运行中出现意外崩溃,无堆栈信息,难以预

22、计出现故障的位置,而且出现的问题很诡异,无法判断原因。排除法:通过把执行代码屏蔽,一步步排除非问题代码,最后达到定位问题范围:此方法特点:需要修改代码和编译;适用于各种平台;无法精确定位和在调试环境中现场调试;会改变执行路径,并不一定总是有效;适用于寻找问题范围; /foo (); A:foo () /a-callSome (); 多线程编程 未知错误的代码定位围栏法困境:线程某一段代码会随机性的崩溃,崩溃后无法看到堆栈,但可以在本地调试环境中重现此问题,能够初步知道错误区间,只是无法知道具体错误位置和产生错误的原因。围栏法:通过捕捉错误的方式,一步步把错误范围缩小,直到最后定位到错误位置:t

23、ry foo (); catch () int I = 0; if (I = 0) +I;此方法特点:需要修改代码和编译;适用于Windows平台;适用于调试模式;可精确定位问题所在,然后拖动调试指针重复执行;A:foo ()try a-callSome ();catch () int I = 0; if (I = 0) +I;多线程编程 未知错误的代码定位Step法困境:某一段代码总是会偶然的错误,但是不知道产生错误的具体代码行和原因,因为是随机性问题,而且往往在调试环境中难以出现(调试环境毕竟受开发设备条件制约),所以前面所述的调试方法都无效。Step法:在运行过程中跟踪执行路径,且写的输出信息代码来不影响执行性能(这也可能会改变多线程执行路径):int step = 0;try step = 1; step = 2; catch (Exception&) printf (addDataBlock: step is =%dn, step);throw;此方法特点:适用于非调试模式;几乎不影响正常执行的性能;可精确跟踪问题多线程编程

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!