文库网
ImageVerifierCode 换一换
首页 文库网 > 资源分类 > DOC文档下载
分享到微信 分享到微博 分享到QQ空间

多线程的那点儿事.doc

  • 资源ID:5896       资源大小:438KB        全文页数:44页
  • 资源格式: DOC       下载:注册后免费下载
微信登录下载
快捷下载 游客一键下载
账号登录下载
三方登录下载: QQ登录 微博登录
二维码
扫码关注公众号登录
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
如填写123,账号就是123,密码也是123。
验证码:   换一换

加入VIP,免费下载
 
账号:
密码:
验证码:   换一换
  忘记密码?
    
友情提示
2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

多线程的那点儿事.doc

1、多线程的那点儿事(之大结局)多线程一直是我比较喜欢的话题,当然也是很多朋友比较害怕的话题。喜欢它,因为它确实可以提高 pc 的使用效率;讨厌它,因为如果对它处理不好,反而会导致更大的麻烦。这里断断续续写了这么多,没有什么新意,主要是想结合自己这么多年的个人经历谈一谈自己的想法而已。真心希望这些文章能够达到抛砖引玉的效果,更多的达人可以奉献出自己的经验和方法。谢谢(01) 多线程的哪些儿事(优先级反转)(02)多线程的哪些儿事(多核编程)(03) 多线程的哪些儿事(多线程数据结构)(04) 多线程的哪些儿事(无锁链表)(05)多线程的哪些儿事(顺序锁)(06)多线程的哪些儿事(无锁队列)(07)

2、多线程的哪些儿事(多线程调试)(08)多线程的哪些儿事(避免死锁)(09) 多线程的哪些儿事(生产者- 消费者)(10)多线程的哪些儿事(嵌套锁)(11)多线程的哪些儿事(读写锁)(12)多线程的哪些儿事(原子锁)(13) 多线程的哪些儿事(C+锁)(14) 多线程的哪些儿事(windows 锁)(15)多线程的哪些儿事(自旋锁)(16)多线程的哪些儿事(数据互斥)(17)多线程的哪些儿事(数据同步)(18)多线程的哪些儿事(死锁)(19)多线程的哪些儿事(基础篇)多线程的哪些儿事(基础篇) 。多线程编程是现代软件技术中很重要的一个环节。要弄懂多线程,这就要牵涉到多进程?当然,要了解到多进程,

3、就要涉及到操作系统。不过大家也不要紧张,听我慢慢道来。这其中的环节其实并不复杂。(1)单 CPU 下的多线程在没有出现多核 CPU 之前,我们的计算资源是唯一的。如果系统中有多个任务要处理的话,那么就需要按照某种规则依次调度这些任务进行处理。什么规则呢?可以是一些简单的调度方法,比如说1)按照优先级调度2)按照 FIFO 调度3)按照时间片调度等等当然,除了 CPU 资源之外,系统中还有一些其他的资源需要共享,比如说内存、文件、端口、socket 等。既然前面说到系统中的资源是有限的,那么获取这些资源的最小单元体是什么呢,其实就是进程。举个例子来说,在 linux 上面每一个享有资源的个体称为

4、 task_struct,实际上和我们说的进程是一样的。我们可以看看 task_struct(linux 0.11 代码)都包括哪些内容,cpp view plaincopy1. struct task_struct 2. /* these are hardcoded - dont touch */ 3. long state; /* -1 unrunnable, 0 runnable, 0 stopped */ 4. long counter; 5. long priority; 6. long signal; 7. struct sigaction sigaction32; 8. long

5、 blocked; /* bitmap of masked signals */ 9. /* various fields */ 10. int exit_code; 11. unsigned long start_code,end_code,end_data,brk,start_stack; 12. long pid,father,pgrp,session,leader; 13. unsigned short uid,euid,suid; 14. unsigned short gid,egid,sgid; 15. long alarm; 16. long utime,stime,cutime

6、,cstime,start_time; 17. unsigned short used_math; 18. /* file system info */ 19. int tty; /* -1 if no tty, so it must be signed */ 20. unsigned short umask; 21. struct m_inode * pwd; 22. struct m_inode * root; 23. struct m_inode * executable; 24. unsigned long close_on_exec; 25. struct file * filpNR

7、_OPEN; 26. /* ldt for this task 0 - zero 1 - cs 2 - ds 28. /* tss for this task */ 29. struct tss_struct tss; 30. ; 每一个 task 都有自己的 pid,在系统中资源的分配都是按照 pid 进行处理的。这也就说明,进程确实是资源分配的主体。这时候,可能有朋友会问了,既然 task_struct 是资源分配的主体,那为什么又出来thread?为什么系统调度的时候是按照 thread 调度,而不是按照进程调度呢?原因其实很简单,进程之间的数据沟通非常麻烦,因为我们之所以把这些进程分开

8、,不正是希望它们之间不要相互影响嘛。假设是两个进程之间数据传输,那么需要如果需要对共享数据进行访问需要哪些步骤呢,1)创建共享内存2)访问共享内存 -系统调用-读取数据3)写入共享内存 -系统调用-写入数据要是写个代码,大家可能就更明白了,cpp view plaincopy1. #include 2. #include 3. 4. int value = 10; 5. 6. int main(int argc, char* argv) 7. 8. int pid = fork(); 9. if(!pid) 10. Value = 12; 11. return 0; 12. 13. print

9、f(“value = %dn“, value); 14. return 1; 15. 上面的代码是一个创建子进程的代码,我们发现打印的 value 数值还是 10。尽管中间创建了子进程,修改了 value 的数值,但是我们发现打印下来的数值并没有发生改变,这就说明了不同的进程之间内存上是不共享的。那么,如果修改成 thread 有什么好处呢?其实最大的好处就是每个 thread 除了享受单独cpu 调度的机会,还能共享每个进程下的所有资源。要是调度的单位是进程,那么每个进程只能干一件事情,但是进程之间是需要相互交互数据的,而进程之间的数据都需要系统调用才能应用,这在无形之中就降低了数据的处理效

10、率。(2)多核 CPU 下的多线程没有出现多核之前,我们的 CPU 实际上是按照某种规则对线程依次进行调度的。在某一个特定的时刻,CPU 执行的还是某一个特定的线程。然而,现在有了多核 CPU,一切变得不一样了,因为在某一时刻很有可能确实是 n 个任务在 n 个核上运行。我们可以编写一个简单的 open mp 测试一下,如果还是一个核,运行的时间就应该是一样的。cpp view plaincopy1. #include 2. #define MAX_VALUE 10000000 3. 4. double _test(int value) 5. 6. int index; 7. double r

11、esult; 8. 9. result = 0.0; 10. for(index = value + 1; index 2. #include 3. #include 4. 5. unsigned int value = 0; 6. 7. void print(void* argv) 8. 9. while(1) 10. printf(“ 11. value +; 12. Sleep(1000); 13. 14. 15. 16. int main() 17. 18. _beginthread( print, 0, NULL ); 19. _beginthread( print, 0, NULL

12、); 20. 21. while(1) 22. Sleep(0); 23. 24. return 1; 25. 注意,在 VC 上面编译的时候,需要打开/MD 开关。具体操作为,【project】-【setting】-【c/c+】-Category【Code Generation】-【Use run-time library】-【Debug Multithreaded】即可。通过上面的示例,我们看到作为共享变量的 value 事实上是可以被所有的线程访问的。这就是线程数据同步的最大优势 方便,直接。因为线程之间除了堆栈空间不一样之外,代码段和数据段都是在一个空间里面的。所以,线程想访问公共数据

13、,就可以访问公共数据,没有任何的限制。当然,事物都有其两面性。这种对公共资源的访问模式也会导致一些问题。什么问题呢?我们看了就知道了。现在假设有一个池塘,我们雇两个人来喂鱼。两个人不停地对池塘里面的鱼进行喂食。我们规定在一个人喂鱼的时候,另外一个人不需要再喂鱼,否则鱼一次喂两回就要撑死了。为此,我们安装了一个牌子作为警示。如果一个人在喂鱼,他会把牌子设置为 FALSE,那么另外一个人看到这个牌子,就不会继续喂鱼了。等到这个人喂完后,他再把牌子继续设置为 TRUE。如果我们需要把这个故事写成代码,那么怎么写呢?朋友们试试看,cpp view plaincopy1. while(1) 2. if(

14、 flag = true) 3. flag = false; 4. do_give_fish_food(); 5. flag = true; 6. 7. 8. Sleep(0); 9. 上面的代码看上去没有问题了,但是大家看看代码的汇编代码,看看是不是存在隐患。因为还会出现两个人同时喂食的情况,cpp view plaincopy1. 23: while(1) 2. 004010E8 mov eax,1 3. 004010ED test eax,eax 4. 004010EF je do_action+56h (00401126) 5. 24: if( flag = true) 6. 0040

15、10F1 cmp dword ptr flag (00433e04),1 7. 004010F8 jne do_action+43h (00401113) 8. 25: flag = false; 9. 004010FA mov dword ptr flag (00433e04),0 10. 26: do_give_fish_food(); 11. 00401104 call ILT+15(do_give_fish_food) (00401014) 12. 27: flag = true; 13. 00401109 mov dword ptr flag (00433e04),1 14. 28:

16、 15. 29: 16. 30: Sleep(0); 17. 00401113 mov esi,esp 18. 00401115 push 0 19. 00401117 call dword ptr _imp_Sleep4 (004361c4) 20. 0040111D cmp esi,esp 21. 0040111F call _chkesp (004011e0) 22. 31: 23. 00401124 jmp do_action+18h (004010e8) 24. 32: 我们此时假设有两个线程 a 和 b 在不停地进行判断和喂食操作。设置当前 flag = true,此时线程 a 执

17、行到 004010F8 处时,判断鱼还没有喂食,正准备执行指令 004010F8,但是还没有来得及对 falg 进行设置,此时出现了线程调度。线程 b 运行到 004010F8 时,发现当前没有人喂食,所以执行喂食操作。等到 b 线程喂食结束,运行到 00401113 的时候,此时又出现了调度。线程 a 有继续运行,因为之前已经判断了当前还没有喂食,所以线程 a 继续进行了喂食了操作。所以,可怜的鱼,这一次就连续经历了两次喂食操作,估计有一部分鱼要撑死了。当然鱼在这里之所以会出现撑死的情况,主要是因为 line 24 和 line 25 之间出现了系统调度。所以,我们在编写程序的时候必须有一个

18、牢固的思想意识,如果缺少必须要的手段,程序可以任何时刻任何地点被调度,那此时公共数据的计算就会出现错误。那么有没有方法避免这种情况的发生呢?当然有。朋友们可以继续关注下面的博客。多线程的哪些儿事(数据互斥)在多线程存在的环境中,除了堆栈中的临时数据之外,所有的数据都是共享的。如果我们需要线程之间正确地运行,那么务必需要保证公共数据的执行和计算是正确的。简单一点说,就是保证数据在执行的时候必须是互斥的。否则,如果两个或者多个线程在同一时刻对数据进行了操作,那么后果是不可想象的。也许有的朋友会说,不光数据需要保护,代码也需要保护。提出这个观点的朋友只看到了数据访问互斥的表象。在程序的运行空间里面,

19、什么最重要的呢?代码吗?当然不是。代码只是为了数据的访问存在的。数据才是我们一切工作的出发点和落脚点。那么,有什么办法可以保证在某一时刻只有一个线程对数据进行操作呢?四个基本方法:(1)关中断(2)数学互斥方法(3)操作系统提供的互斥方法(4)cpu 原子操作为了让大家可以对这四种方法有详细的认识,我们可以进行详细的介绍。(1)关中断要让数据在某一时刻只被一个线程访问,方法之一就是停止线程调度就可以了。那么怎样停止线程调度呢?那么关掉时钟中断就可以了啊。在 X86 里面的确存在这样的两个指令,cpp view plaincopy1. #include 2. 3. int main() 4. 5

20、. _asm 6. cli 7. sti 8. 9. return 1; 10. 其中 cli 是关中断,sti 是开中断。这段代码没有什么问题,可以编过,当然也可以生成执行文件。但是在执行的时候会出现一个异常告警:Unhandled exception in test.exe: 0xC0000096: Privileged Instruction。告警已经说的很清楚了,这是一个特权指令。只有系统或者内核本身才可以使用这个指令。不过,大家也可以想象一下。因为平常我们编写的程序都是应用级别的程序,要是每个程序都是用这些代码,那不乱了套了。比如说,你不小心安装一个低质量的软件,说不定什么时候把你的中断关了,这样你的网络就断了,你的输入就没有回应了,你的音乐什么都没有了,这样的环境你受的了吗?应用层的软件是千差万别的,软件的水平也是参差不齐的,所以系统不可能相信任何一个私有软件,它相信的只是它自己。(2)数学方法假设有两个线程(a、b)正要对一个共享数据进行访问,那么怎么做到他们之间的互斥的呢?其实我们可以这么做,


注意事项

本文(多线程的那点儿事.doc)为本站会员(李静文)主动上传,文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知文库网(点击联系客服),我们立即给予删除!




关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

文库网用户QQ群:731843829  微博官方号:文库网官方   知乎号:文库网

Copyright© 2025 文库网 wenkunet.com 网站版权所有世界地图

经营许可证编号:粤ICP备2021046453号   营业执照商标

1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png 10.png