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

    作者回复: 赞,太赞啦

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

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

    作者回复: 赞,对的

     3
     25
  • why
    2019-04-19
    - 线程复制执行二进制指令
    - 多进程缺点: 创建进程占用资源多; 进程间通信需拷贝内存, 不能共享
    - 线程相关操作
        - 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
    展开
    
     8
  • 赵又新
    2019-04-19
    手敲了第一个代码,报未定义变量的错,查资料发现文章中PTHREAD_CTREATE_JOINABL打错了,末尾少了个E

    作者回复: 是的,赞

    
     6
  • 一只特立独行的猪
    2019-05-04
    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

     4
     5
  • 张志强
    2019-04-19
    这一章干货很多,操作系统课上都要分好几节讲。消化需要不少时间

    作者回复: 是的

    
     5
  • 刘強
    2019-04-19
    凌晨学习!

    作者回复: 牛

    
     5
  • 算不出流源
    2019-04-19
    刘老师,关于条件变量和互斥锁配合使用的一个问题,既然pthread_mutex_lock() 本身就是阻塞的,那么在你举的老板招聘三个员工抢活干的例子中为什么还需要再适使用条件变量pthread_cond_wait()来等活干呢,条件变量等待的时候不也是阻塞的吗?没有理解增加条件变量的意义,请老师指导
     1
     4
  • Geek_f9e53a
    2019-04-19
    线程栈上的本地数据和线程私有数据有什么本质区别,能否举例说明?谢谢

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

    
     2
  • 不一样的烟火
    2019-04-28
    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);
    }
    这段其实有个疑问,上一个线程执行完加锁等待任务之后,下一个线程为什么还可以打印自己在等待任务,打印那一段不是已经被锁住了吗
    展开
     2
     1
  • 唐龙
    2019-04-20
    本节的创建五条线程模拟文件下载的例子,我这里操作有一些问题。首先,我在主线程里使用同一个变量downloadtime接收子线程的返回值的时候,输出和预想的不一样。thread 0返回时没有问题,thread 1返回时会输出两条,downloadtime一样但是前面会把thread0 file0重新输出一遍再输出一遍thread1 file1……thread4返回时会输出五条,五个一样的downloadtime,一条thread0 file0,一条thread1 file1……最后一条一条thread4 file4。但是如果我把downloadtime设置为一个数组,即使用不同的地址,就不会出现这个问题了。我想知道出现前面情况的原因是什么。

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

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

    作者回复: 少了一个E

    
     1
  • 罗乾林
    2019-04-19
    刘老师,在boss分配任务的例子中退出时主线程设置quit = 1,叫醒其他等待的线程之后就直接销毁了g_task_lock,g_task_cv,有可能之后coder 重新获取锁,检测到quit==1 然后释放锁。这种情况下我认为锁都被销毁了可能会出问题,代码测试了下,发现程序没出问题。不明白这是什么原因?
    
     1
  • 罗乾林
    2019-04-19
    @算不出流源 我的理解pthread_cond_wait 调用之后是会让出互锁,避免占着资源不干活,其他线程才可能获取该锁。对应这个例子就是Boss拿不到锁无法进行任务分配

    作者回复: 是的

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

    作者回复: 加油

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

    作者回复: 后面会讲的

    
     1
  • 忆水寒
    2020-02-02
    其实看看Java里面JUC的源码,再来看这个并发组件,大概都能明白。因为Java底层也是依赖linux系统库
    
    
  • 天才小飞猫
    2020-02-01
    刘老师,最后一个例子中只有一个boss,那boss在broadcast任务时可以不加锁么?
    
    
  • 何柄融
    2020-01-29
    不就是wait和notify吗?
    
    
我们在线,来聊聊吧