趣谈Linux操作系统
刘超
网易杭州研究院云计算技术部首席架构师
立即订阅
19261 人已学习
课程目录
已完结 72 讲
0/4登录后,你可以任选4讲全文学习。
入门准备篇 (3讲)
开篇词 | 为什么要学习Linux操作系统?
免费
01 | 入学测验:你究竟对Linux操作系统了解多少?
02 | 学习路径:爬过这六个陡坡,你就能对Linux了如指掌
核心原理篇:第一部分 Linux操作系统综述 (3讲)
03 | 你可以把Linux内核当成一家软件外包公司的老板
04 | 快速上手几个Linux命令:每家公司都有自己的黑话
05 | 学会几个系统调用:咱们公司能接哪些类型的项目?
核心原理篇:第二部分 系统初始化 (4讲)
06 | x86架构:有了开放的架构,才能打造开放的营商环境
07 | 从BIOS到bootloader:创业伊始,有活儿老板自己上
08 | 内核初始化:生意做大了就得成立公司
09 | 系统调用:公司成立好了就要开始接项目
核心原理篇:第三部分 进程管理 (10讲)
10 | 进程:公司接这么多项目,如何管?
11 | 线程:如何让复杂的项目并行执行?
12 | 进程数据结构(上):项目多了就需要项目管理系统
13 | 进程数据结构(中):项目多了就需要项目管理系统
14 | 进程数据结构(下):项目多了就需要项目管理系统
15 | 调度(上):如何制定项目管理流程?
16 | 调度(中):主动调度是如何发生的?
17 | 调度(下):抢占式调度是如何发生的?
18 | 进程的创建:如何发起一个新项目?
19 | 线程的创建:如何执行一个新子项目?
核心原理篇:第四部分 内存管理 (7讲)
20 | 内存管理(上):为客户保密,规划进程内存空间布局
21 | 内存管理(下):为客户保密,项目组独享会议室封闭开发
22 | 进程空间管理:项目组还可以自行布置会议室
23 | 物理内存管理(上):会议室管理员如何分配会议室?
24 | 物理内存管理(下):会议室管理员如何分配会议室?
25 | 用户态内存映射:如何找到正确的会议室?
26 | 内核态内存映射:如何找到正确的会议室?
核心原理篇:第五部分 文件系统 (4讲)
27 | 文件系统:项目成果要归档,我们就需要档案库
28 | 硬盘文件系统:如何最合理地组织档案库的文档?
29 | 虚拟文件系统:文件多了就需要档案管理系统
30 | 文件缓存:常用文档应该放在触手可得的地方
核心原理篇:第六部分 输入输出系统 (5讲)
31 | 输入与输出:如何建立售前售后生态体系?
32 | 字符设备(上):如何建立直销模式?
33 | 字符设备(下):如何建立直销模式?
34 | 块设备(上):如何建立代理商销售模式?
35 | 块设备(下):如何建立代理商销售模式?
核心原理篇:第七部分 进程间通信 (7讲)
36 | 进程间通信:遇到大项目需要项目组之间的合作才行
37 | 信号(上):项目组A完成了,如何及时通知项目组B?
38 | 信号(下):项目组A完成了,如何及时通知项目组B?
39 | 管道:项目组A完成了,如何交接给项目组B?
40 | IPC(上):不同项目组之间抢资源,如何协调?
41 | IPC(中):不同项目组之间抢资源,如何协调?
42 | IPC(下):不同项目组之间抢资源,如何协调?
核心原理篇:第八部分 网络系统 (7讲)
43 预习 | Socket通信之网络协议基本原理
43 | Socket通信:遇上特大项目,要学会和其他公司合作
44 | Socket内核数据结构:如何成立特大项目合作部?
45 | 发送网络包(上):如何表达我们想让合作伙伴做什么?
46 | 发送网络包(下):如何表达我们想让合作伙伴做什么?
47 | 接收网络包(上):如何搞明白合作伙伴让我们做什么?
48 | 接收网络包(下):如何搞明白合作伙伴让我们做什么?
核心原理篇:第九部分 虚拟化 (7讲)
49 | 虚拟机:如何成立子公司,让公司变集团?
50 | 计算虚拟化之CPU(上):如何复用集团的人力资源?
51 | 计算虚拟化之CPU(下):如何复用集团的人力资源?
52 | 计算虚拟化之内存:如何建立独立的办公室?
53 | 存储虚拟化(上):如何建立自己保管的单独档案库?
54 | 存储虚拟化(下):如何建立自己保管的单独档案库?
55 | 网络虚拟化:如何成立独立的合作部?
核心原理篇:第十部分 容器化 (4讲)
56 | 容器:大公司为保持创新,鼓励内部创业
57 | Namespace技术:内部创业公司应该独立运营
58 | CGroup技术:内部创业公司应该独立核算成本
59 | 数据中心操作系统:上市敲钟
实战串讲篇 (9讲)
60 | 搭建操作系统实验环境(上):授人以鱼不如授人以渔
61 | 搭建操作系统实验环境(下):授人以鱼不如授人以渔
62 | 知识串讲:用一个创业故事串起操作系统原理(一)
63 | 知识串讲:用一个创业故事串起操作系统原理(二)
64 | 知识串讲:用一个创业故事串起操作系统原理(三)
65 | 知识串讲:用一个创业故事串起操作系统原理(四)
66 | 知识串讲:用一个创业故事串起操作系统原理(五)
67 | 期末测试:这些操作系统问题,你真的掌握了吗?
结束语 | 永远别轻视任何技术,也永远别轻视自己
免费
专栏加餐 (2讲)
学习攻略(一):学好操作系统,需要掌握哪些前置知识?
“趣谈Linux操作系统”食用指南
免费
趣谈Linux操作系统
登录|注册

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

刘超 2019-04-19
上一节我们讲了如何创建进程,这一节我们来看如何创建线程。

为什么要有线程?

其实,对于任何一个进程来讲,即便我们没有主动去创建线程,进程也是默认有一个主线程的。线程是负责执行二进制指令的,它会根据项目执行计划书,一行一行执行下去。进程要比线程管的宽多了,除了执行指令之外,内存、文件系统等等都要它来管。
所以,进程相当于一个项目,而线程就是为了完成项目需求,而建立的一个个开发任务。默认情况下,你可以建一个大的任务,就是完成某某功能,然后交给一个人让它从头做到尾,这就是主线程。但是有时候,你发现任务是可以拆解的,如果相关性没有非常大前后关联关系,就可以并行执行。
例如,你接到了一个开发任务,要开发 200 个页面,最后组成一个网站。这时候你就可以拆分成 20 个任务,每个任务 10 个页面,并行开发。都开发完了,再做一次整合,这肯定比依次开发 200 个页面快多了。
那我们能不能成立多个项目组实现并行开发呢?当然可以了,只不过这样做有两个比较麻烦的地方。
第一个麻烦是,立项。涉及的部门比较多,总是劳师动众。你本来想的是,只要能并行执行任务就可以,不需要把会议室都搞成独立的。另一个麻烦是,项目组是独立的,会议室是独立的,很多事情就不受你控制了,例如一旦有了两个项目组,就会有沟通问题。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《趣谈Linux操作系统》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(61)

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

    作者回复: 赞,太赞啦

    2019-04-19
    1
    76
  • 安排
    很多同学不理解这个BOSS给员工分任务的场景为什么要用条件变量,因为用互斥量也可以实现。员工等在互斥量上一样会进入睡眠,等BOSS释放互斥锁时也会唤醒这些员工。这样看来根本没有用条件变量的必要。

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

    作者回复: 赞,对的

    2019-04-19
    3
    22
  • why
    - 线程复制执行二进制指令
    - 多进程缺点: 创建进程占用资源多; 进程间通信需拷贝内存, 不能共享
    - 线程相关操作
        - pthread_exit(A), A 是线程退出的返回值
        - pthread_attr_t 线程属性, 用辅助函数初始化并设置值; 用完需要销毁
        - pthread_create 创建线程, 四个参数(线程对象, 属性, 运行函数, 运行参数)
        - pthread_join 获取线程退出返回值, 多线程依赖 libpthread.so
        - 一个线程退出, 会发送信号给 其他所有同进程的线程
    - 线程中有三类数据
        - 线程栈本地数据, 栈大小默认 8MB; 线程栈之间有保护间隔, 若误入会引发段错误
        - 进程共享的全局数据
        - 线程级别的全局变量(线程私有数据, pthread_key_create(key, destructer)); key 所有线程都可以访问, 可填入各自的值(同名不同值的全局变量)
    - 数据保护
        - Mutex(互斥), 初始化; lock(没抢到则阻塞)/trylock(没抢到则返回错误码); unlock; destroy
        - 条件变量(通知), 收到通知, 还是要抢锁(由 wait 函数执行); 因此条件变量与互斥锁配合使用
        - 互斥锁所谓条件变量的参数, wait 函数会自动解锁/加锁
        - broadcast(通知); destroy
    2019-04-19
    7
  • 赵又新
    手敲了第一个代码,报未定义变量的错,查资料发现文章中PTHREAD_CTREATE_JOINABL打错了,末尾少了个E

    作者回复: 是的,赞

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

    作者回复: 牛

    2019-04-19
    5
  • 一只特立独行的猪
    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
    4
    4
  • 算不出流源
    刘老师,关于条件变量和互斥锁配合使用的一个问题,既然pthread_mutex_lock() 本身就是阻塞的,那么在你举的老板招聘三个员工抢活干的例子中为什么还需要再适使用条件变量pthread_cond_wait()来等活干呢,条件变量等待的时候不也是阻塞的吗?没有理解增加条件变量的意义,请老师指导
    2019-04-19
    1
    4
  • 张志强
    这一章干货很多,操作系统课上都要分好几节讲。消化需要不少时间

    作者回复: 是的

    2019-04-19
    4
  • 不一样的烟火
    while(!quit){

        pthread_mutex_lock(&g_task_lock);
        while(tail == head){
          if(quit){
            pthread_mutex_unlock(&g_task_lock);
            pthread_exit((void *)0);
          }
          printf("No task now! Thread %u is waiting!\n", (unsigned int)tid);
          pthread_cond_wait(&g_task_cv, &g_task_lock);
          printf("Have task now! Thread %u is grabing the task !\n", (unsigned int)tid);
        }
        char task = tasklist[head++];
        pthread_mutex_unlock(&g_task_lock);
        printf("Thread %u has a task %c now!\n", (unsigned int)tid, task);
        sleep(5);
        printf("Thread %u finish the task %c!\n", (unsigned int)tid, task);
    }
    这段其实有个疑问,上一个线程执行完加锁等待任务之后,下一个线程为什么还可以打印自己在等待任务,打印那一段不是已经被锁住了吗
    2019-04-28
    2
    1
  • 唐龙
    本节的创建五条线程模拟文件下载的例子,我这里操作有一些问题。首先,我在主线程里使用同一个变量downloadtime接收子线程的返回值的时候,输出和预想的不一样。thread 0返回时没有问题,thread 1返回时会输出两条,downloadtime一样但是前面会把thread0 file0重新输出一遍再输出一遍thread1 file1……thread4返回时会输出五条,五个一样的downloadtime,一条thread0 file0,一条thread1 file1……最后一条一条thread4 file4。但是如果我把downloadtime设置为一个数组,即使用不同的地址,就不会出现这个问题了。我想知道出现前面情况的原因是什么。

    作者回复: 我这里是对的呀

    2019-04-20
    1
  • WL
    老师请问一下, 我编译文章中的例子时报错: error: 'PTHREAD_CREATE_JOINABL' undeclared (first use in this function) 这个应该咋解决

    作者回复: 少了一个E

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

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

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

    作者回复: 是的

    2019-04-19
    1
  • 🗿顾晓峰🈹🈳🈴🈷🎏
    学习了
    2019-04-19
    1
  • ninuxer
    打卡day12
    线程这篇,算是看懂了😂

    作者回复: 加油

    2019-04-19
    1
  • Geek_082575
    刘老师可以再深入讲解线程的实现吗

    作者回复: 后面会讲的

    2019-04-19
    1
  • 陈志恒
    这一节讲了多线程编程的套路,但是我没有对于每一个函数进行详细的讲解。有点感觉像是系统函数手册!
    2019-11-25
  • 阿西吧
    不加锁时的操作也不会出现金额不对,难道跟操作系统版本有关系?ubuntu16.4 X64

    Thread 2136516352 is transfering money!
    Thread 2094556928 is transfering money!
    Thread 2115536640 is transfering money!
    Thread 2105046784 is transfering money!
    Thread 2126026496 is transfering money!
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    ......
    ......
    ......
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200


    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    money_of_tom + money_of_jerry = 200
    Thread 2105046784 finish transfering money!
    Thread 2115536640 finish transfering money!
    Thread 2136516352 finish transfering money!
    Thread 2126026496 finish transfering money!
    Thread 2094556928 finish transfering money!
    2019-11-22
  • Marshall
    老师能说下 用户线程 轻量级进程 内核线程 以及jvm在Linux下生成的线程 。
    2019-11-19
  • kdb_reboot
    再次复习这篇文章,看懂这篇需要知道一件事:线程共享同一地址空间;另外推荐apue,里面的示例代码也很好
    2019-09-23
收起评论
61
返回
顶部