趣谈 Linux 操作系统
刘超
前网易杭州研究院云计算技术部首席架构师
85458 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 72 讲
趣谈 Linux 操作系统
15
15
1.0x
00:00/00:00
登录|注册

11 | 线程:如何让复杂的项目并行执行?

用于通知等待的线程
与互斥锁配合使用
使用pthread_mutex_lock和pthread_mutex_unlock
互斥锁用于保护共享数据
使用pthread_getspecific获取value
使用pthread_setspecific设置value
可通过pthread_key_create创建
需要保护以避免多线程修改
可通过pthread_attr_t修改栈大小
条件变量
Mutex
线程私有数据
共享的全局数据
线程栈上的本地数据
使用pthread_exit退出线程
线程执行函数参数为void指针
使用pthread_create创建线程
线程用于并行执行任务
线程负责执行二进制指令
进程默认有一个主线程
数据的保护
线程的数据
如何创建线程?
为什么要有线程?
线程

该思维导图由 AI 生成,仅供参考

上一节我们讲了如何创建进程,这一节我们来看如何创建线程。

为什么要有线程?

其实,对于任何一个进程来讲,即便我们没有主动去创建线程,进程也是默认有一个主线程的。线程是负责执行二进制指令的,它会根据项目执行计划书,一行一行执行下去。进程要比线程管的宽多了,除了执行指令之外,内存、文件系统等等都要它来管。
所以,进程相当于一个项目,而线程就是为了完成项目需求,而建立的一个个开发任务默认情况下,你可以建一个大的任务,就是完成某某功能,然后交给一个人让它从头做到尾,这就是主线程。但是有时候,你发现任务是可以拆解的,如果相关性没有非常大前后关联关系,就可以并行执行。
例如,你接到了一个开发任务,要开发 200 个页面,最后组成一个网站。这时候你就可以拆分成 20 个任务,每个任务 10 个页面,并行开发。都开发完了,再做一次整合,这肯定比依次开发 200 个页面快多了。
那我们能不能成立多个项目组实现并行开发呢?当然可以了,只不过这样做有两个比较麻烦的地方。
第一个麻烦是,立项。涉及的部门比较多,总是劳师动众。你本来想的是,只要能并行执行任务就可以,不需要把会议室都搞成独立的。另一个麻烦是,项目组是独立的,会议室是独立的,很多事情就不受你控制了,例如一旦有了两个项目组,就会有沟通问题。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了多线程编程的基本概念和实际应用。首先强调了线程的作用和优势,指出线程可以实现任务的并行执行,提高效率。文章详细介绍了线程的创建、执行和退出过程,并给出了示例代码,展示了如何使用线程来实现多个文件的并行下载。此外,还讨论了线程访问的数据细分成三类,包括线程栈上的本地数据、在整个进程里共享的全局数据以及线程私有数据。通过实际示例和清晰的语言,向读者展示了如何利用线程实现项目的并行执行,具有很强的实用性和技术指导意义。文章还介绍了Mutex的使用流程,以及条件变量的作用,通过实例代码展示了它们在多线程编程中的应用。总的来说,本文内容丰富,涵盖了多线程编程的基本概念和实际应用,对于想要深入了解并行计算的读者具有很高的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《趣谈 Linux 操作系统》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(98)

  • 最新
  • 精选
  • 刘強
    两个线程操作同一临界区时,通过互斥锁保护,若A线程已经加锁,B线程再加锁时候会被阻塞,直到A释放锁,B再获得锁运行,进程B必须不停的主动获得锁、检查条件、释放锁、再获得锁、再检查、再释放,一直到满足运行的条件的时候才可以(而此过程中其他线程一直在等待该线程的结束),这种方式是比较消耗系统的资源的。而条件变量同样是阻塞,还需要通知才能唤醒,线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,该线程就休眠了,应该仍阻塞在这里,等待条件满足后被唤醒,节省了线程不断运行浪费的资源。这个过程一般用while语句实现。当线程B发现被锁定的变量不满足条件时会自动的释放锁并把自身置于等待状态,让出CPU的控制权给其它线程。其它线程 此时就有机会去进行操作,当修改完成后再通知那些由于条件不满足而陷入等待状态的线程。这是一种通知模型的同步方式,大大的节省了CPU的计算资源,减少了线程之间的竞争,而且提高了线程之间的系统工作的效率。这种同步方式就是条件变量。 这是网上的一段话。我觉得说的很清楚了。 如果学过java的话,其实就是线程之间的互斥和协作,条件变量就是用来协作的,对应java里的wait()和notify()函数。 我个人觉得读这个专栏必须有一定的基础理论,具体的说起码看过相关的书籍,了解个大概。如果只有一些语言的基础,没有看过相关计算机体系或者操作系统方面的书籍,看起来会很费劲,不知所云。就我自己来说,我看过于渊写的《一个操作系统的实现》,《linux内核设计与实现》《现代操作系统》《intel汇编程序》《深入理解计算机系统》《unix高级环境编程》等,尤其是于渊写的这本书,从计算机加电开始,一直到多进程,进程间通信,从汇编到c语言,都有完整的代码,都是作者自己亲手写的,可操作性极强。还有csapp(深入理解计算机系统)这本书,所有人公认的学计算机必须看的。还有很多,总之我想说的是自己必须去看书学习,仅仅想靠一个专栏的学习来了解一个东西是远远不够的。 话又说回来,你可能会问你看了这么多书,早应该精通了吧,还到这儿来干嘛?其实这就是我最大的问题,书看的太多,理论知道的不少,但是动手实践太少,就是所谓的眼高手低。而且不实践,看的多,忘的多。一方面是工作原因,除非去大公司,哪有机会让你实践底层的技术。二是自己太懒,没有耐性,任何可用的东西都是一行行代码经过千锤百炼形成的。书看了不少仅仅满足了自己的求知欲,却没有弄出任何有用的东西。 来这儿也是想看看理论是如何通过一行行代码落地的,也学学作者的实践经验,加深一些概念的理解。 多看书没错,但效率比较低,从实践中不断总结,思考才是快速成长的正确方法。 道理知道很多,但还是过不好这一生。理论掌握不少,还是写不出有用的代码。悲催!

    作者回复: 赞,太赞啦

    2019-04-19
    14
    238
  • 安排
    很多同学不理解这个BOSS给员工分任务的场景为什么要用条件变量,因为用互斥量也可以实现。员工等在互斥量上一样会进入睡眠,等BOSS释放互斥锁时也会唤醒这些员工。这样看来根本没有用条件变量的必要。 我们可以换一个思路,如果不使用条件变量,而且BOSS也不是一直生产任务,那么这时互斥量就会空闲出来,总会有一个员工能拿到锁,员工线程这时候就会在while循环中不停的获得锁,判断状态,释放锁,这样的话就会十分消耗cpu资源了。 这时候我们可能会想到,在while循环中加个睡眠,例如5秒,也就是员工线程每隔5秒来执行一次获得锁,判断状态,释放锁的操作,这样就会减轻cpu资源的消耗。 但是实际应用场景中,我们无法判断到底间隔几秒来执行一次这个获得锁,判断状态,释放锁的流程,时间长了可能影响吞吐量,时间短了会造成cpu利用率过高,所以这时候引入了条件变量,将主动查询方式改成了被动通知方式,效率也就上去了。 文中的例子很容易让新手迷惑,我也不保证上述理解是对的,希望老师能够指点一二。

    作者回复: 赞,对的

    2019-04-19
    12
    76
  • 紫墨青川
    关于老师的第一段代码我来说一下吧。首先是开发环境,我的是Ubuntu18.04 gcc7.5.0,执行的结果并不正确。最后的输出是这样的: Thread 0 downloads the file file1.avi in 83 minutes. Thread 0 downloads the file file1.avi in 86 minutes. Thread 1 downloads the file file2.rmvb in 86 minutes. Thread 0 downloads the file file1.avi in 77 minutes. Thread 1 downloads the file file2.rmvb in 77 minutes. Thread 2 downloads the file file3.mp4 in 77 minutes. Thread 0 downloads the file file1.avi in 93 minutes. Thread 1 downloads the file file2.rmvb in 93 minutes. Thread 2 downloads the file file3.mp4 in 93 minutes. Thread 3 downloads the file file4.wmv in 93 minutes. Thread 0 downloads the file file1.avi in 15 minutes. Thread 1 downloads the file file2.rmvb in 15 minutes. Thread 2 downloads the file file3.mp4 in 15 minutes. Thread 3 downloads the file file4.wmv in 15 minutes. Thread 4 downloads the file file5.flv in 15 minutes. 我百思不得其解,纯就代码来看,并不复杂,结构也很清晰,并且也没有什么数据竞争。但编译提示给出了线索, 这里有一个警告: old_downloader.c:13:50: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long int’ [-Wformat=] printf("I finish downloading the file within %d minutes!\n", downloadtime); 这时候我就明白了,本该接受long类型的数据,最终的目的是int。每次pthread_join的时候就会再int类型的downloadtime地址写入一个long类型的数据。恰好,int t先于downloadtime压栈,地址和downloadtime在其高字节相邻(栈从高字节到低字节扩张)。所以每次获取downloadtime的时候修改了t,而t控制了循环,因此导致输出的问题。 修改:把int downloadtime修改为long downloadtime就迎刃而解了。 所以对于c编程来说,理解细节很重要,确保要解决编译过程中的所有warning,否则可能会存在未知的错误。

    作者回复: 牛

    2020-05-21
    6
    35
  • 张志强
    这一章干货很多,操作系统课上都要分好几节讲。消化需要不少时间

    作者回复: 是的

    2019-04-19
    13
  • 一只特立独行的猪
    macOS下执行有不同的结果: creating thread 0, please help me to download file1.avi creating thread 1, please help me to download file2.rmvb creating thread 2, please help me to download file3.mp4 I am downloading the file file1.avi! I am downloading the file file2.rmvb! creating thread 3, please help me to download file4.wmv I am downloading the file file3.mp4! creating thread 4, please help me to download file5.flv I am downloading the file file4.wmv! I am downloading the file file5.flv! I finish downloading the file within 7 minutes! I finish downloading the file within 49 minutes! I finish downloading the file within 73 minutes! I finish downloading the file within 58 minutes! I finish downloading the file within 30 minutes! Thread 0 downloads the file file1.avi in 49 minutes. Thread 0 downloads the file file1.avi in 7 minutes. Thread 1 downloads the file file2.rmvb in 7 minutes. Thread 0 downloads the file file1.avi in 73 minutes. Thread 1 downloads the file file2.rmvb in 73 minutes. Thread 2 downloads the file file3.mp4 in 73 minutes. Thread 0 downloads the file file1.avi in 58 minutes. Thread 1 downloads the file file2.rmvb in 58 minutes. Thread 2 downloads the file file3.mp4 in 58 minutes. Thread 3 downloads the file file4.wmv in 58 minutes. Thread 0 downloads the file file1.avi in 30 minutes. Thread 1 downloads the file file2.rmvb in 30 minutes. Thread 2 downloads the file file3.mp4 in 30 minutes. Thread 3 downloads the file file4.wmv in 30 minutes. Thread 4 downloads the file file5.flv in 30 minutes.

    作者回复: 哦,没有mac

    2019-05-04
    9
    8
  • 赵又新
    手敲了第一个代码,报未定义变量的错,查资料发现文章中PTHREAD_CTREATE_JOINABL打错了,末尾少了个E

    作者回复: 是的,赞

    2019-04-19
    8
  • 刘強
    凌晨学习!

    作者回复: 牛

    2019-04-19
    7
  • Geek_f9e53a
    线程栈上的本地数据和线程私有数据有什么本质区别,能否举例说明?谢谢

    作者回复: 相当于全局变量,可以在多个函数间共享,比如线程里面有多个函数

    2019-04-19
    2
    5
  • 罗 乾 林
    @算不出流源 我的理解pthread_cond_wait 调用之后是会让出互锁,避免占着资源不干活,其他线程才可能获取该锁。对应这个例子就是Boss拿不到锁无法进行任务分配

    作者回复: 是的

    2019-04-19
    3
  • garlic
    看了下老师推荐的书,网上有找些资料,整理了下相关API example https://garlicspace.com/2019/06/20/posix-threads-api-整理/

    作者回复: 赞

    2019-06-20
    2
收起评论
显示
设置
留言
98
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部