• 可以试下 oss 的 x-oss-process

  • Ruby 3 Fiber 变化前瞻 at August 03, 2020

    有兴趣可以看下 https://ruby-china.org/topics/29041 我 16 年的帖子

  • Ruby 3 Fiber 变化前瞻 at August 03, 2020

    用了 fiber, 会希望所有的 io 操作都能基于 fiber 调度,mysql 这块重头戏,这块整体底层的 io 调度如果不支持 fiber 调度,那 fiber 带来的整体的改进,大概和也就和 eventmachine 差不多...

    mysql 的 c 库里面,调用 io 操作的时候会有 global vm lock 的锁的 (之前看的时候还是这样)

  • Ruby 3 Fiber 变化前瞻 at July 30, 2020

    好久没关注 Fiber 了... mysql2 这样的大热门的 Gem 有生产可用的 fiber 实现了么?没有的话...很难推广...

  • 这么多年啦... 好久没关注了

  • zk, etcd 都可以

  • 机器多,就老老实实上分布式配置管理吧...

    https://github.com/ctripcorp/apollo

    机器多了,你配置生效还得走灰度...

  • 基于 zk 的 watch 机制,可能会更好,而且,也算是分布式的解决方案

  • 哥们,果然你很活跃呀😀

  • 你这个代码的线程调度是不会影响 @a@c的值得,可以用这个代码来验证

    @a = 1
    r = []
    10.times do |e|
    
    Thread.new {
       @c = 1
       @c += @a
       r << [e, @c]
    }
    end
    

    r 里面的值,虽然 e 的前后顺序不一样,但是,@c的值是一致的

    但是你在@c=1@c += @a中间加一个 puts 的话,这个就会触发 gil 的 lock, 数据异常了

    @a = 1
    r = []
    10.times do |e|
    
    Thread.new {
       @c = 1
       puts 1
       @c += @a
       r << [e, @c]
    }
    end
    
  • 你这个答案是靠谱的,其实题主最后一个代码是不存在线程安全问题的

    GIL 的工作机制不像大家想象的一样,在 ruby 代码的任何一个点都会切换到另外一个线程去工作

    而是有几个明确的工作点的

    • 方法的调用和方法的返回,在这两个地方都会检查一下当前线程的 gil 的锁是否超时,是否要调度到另外线程去工作
    • 所有 io 相关的操作,也会释放 gil 的锁让其它线程来工作
    • 在 c 扩展的代码中手动释放 gil 的锁
    • 还有一个比较难理解,就是 ruby stack 进入 c stack 的时候也会触发 gil 的检测
    static void
    gvl_acquire_common(rb_vm_t *vm)
    {
        if (vm->gvl.acquired) {
    
        vm->gvl.waiting++;
        if (vm->gvl.waiting == 1) {
            /*
             * Wake up timer thread iff timer thread is slept.
             * When timer thread is polling mode, we don't want to
             * make confusing timer thread interval time.
             */
            rb_thread_wakeup_timer_thread_low();
        }
    
        while (vm->gvl.acquired) {
            native_cond_wait(&vm->gvl.cond, &vm->gvl.lock);
        }
    
        vm->gvl.waiting--;
    
        if (vm->gvl.need_yield) {
            vm->gvl.need_yield = 0;
            native_cond_signal(&vm->gvl.switch_cond);
        }
        }
    
        vm->gvl.acquired = 1;
    }
    
    static void
    gvl_acquire(rb_vm_t *vm, rb_thread_t *th)
    {
        native_mutex_lock(&vm->gvl.lock);
        gvl_acquire_common(vm);
        native_mutex_unlock(&vm->gvl.lock);
    }
    
    static void
    gvl_release_common(rb_vm_t *vm)
    {
        vm->gvl.acquired = 0;
        if (vm->gvl.waiting > 0)
        native_cond_signal(&vm->gvl.cond);
    }
    
    static void
    gvl_release(rb_vm_t *vm)
    {
        native_mutex_lock(&vm->gvl.lock);
        gvl_release_common(vm);
        native_mutex_unlock(&vm->gvl.lock);
    }
    

    这几个方法是用来控制 gil 的锁的,可以参考一下 gil 在 c 里面的调用情况

  • 有兴趣的话答个题我们来聊聊

  • 额,这个重点从来不是你答的什么,而是从你答得什么中我们聊出来的那些😀

  • 根据您的面试题的代码,我们才可以更好展开了解您的技能域 也让面试不变的太清谈么

    😄

  • #29 楼 @adam_chen 手机型号是啥,android 版本是啥?

  • 如果只是为了解决 assets precompile 的话...干净整洁,也不用编译这么大的一个 V8

  • 我觉得如果可以接受的话,不如在机器上装个 nodejs,然后去掉 therubyracer

  • ngx_mruby Benchmark at March 18, 2016

    #5 楼 @42thcoder mac 确实不可信,

  • ngx_mruby Benchmark at March 18, 2016

    #3 楼 @nouse ab 为啥不可信,其他都可以理解

  • 咦,露脸了...O(∩_∩)O 哈哈~

  • #33 楼 @ericguo rainbows 也可以呀,模式选 ThreadPool 模式

  • #16 楼 @mizuhashi em 有纯 ruby 实现的,也有 java 和 c 实现的,默认基本是 C 实现的

  • #13 楼 @mizuhashi 不一样,你这个是线程和 FIBER 的整合,而且后面用 while 的方式等待 fiber 执行完非常没有效率

    EM 维护了一个 event loop 当 Fiber 执行完毕了,会进入 EM 的 event loop 唤醒 fiber

  • #9 楼 @rogerluo410 这个是典型 java 的 netty 的方式,但是 ruby 有 GIL,效果不太好

  • #8 楼 @pathbox 确实,在现在看来,大多数情况是这样的

  • #1 楼 @embbnux 手快了....

  • #5 楼 @vincent ^_^

    #6 楼 @pathbox 相对于多进程而言,是的,但是,相对于多线程而言,这个需要分场景了 大部分时候应该是多线程优势比较大,暂时是这么觉得的

    但是,没办法确定EventMachine and Fiber的解决方案比多线程慢的原因是 fiber 导致的还是 eventmachine 导致的,毕竟在 fiber 的方案中,socket 是由 eventmachine 管理的

    有时间,我再写一点详细的 benchmark,确定下到底是不是 Em 导致的性能降低

  • #3 楼 @watraludru

    1. 可能是,我去改改,避免大家误解
    2. require 'fiber' 确实会改进当前的线程代码的运行模式,也可能是在后面第一个 fiberresume 的时候做,他会在当前线程上初始化一个 root fiber,这个时候,这个线程之后的代码都可以理解是运行在这个 root fiber 上了,因为这个 root fiber 还有一定的特殊性,所以单独提出来将,希望不要和普通的 fiber 的理解混在一起,具体的话就会深入非常具体的 fiber 的运行机制,展开讲我也吃不消了,^_^,具体可以见这个代码:
    if (th->fiber) {
    GetFiberPtr(th->fiber, fib);
    cont_save_thread(&fib->cont, th);
    }
    else {
    /* create current fiber */
    fib = root_fiber_alloc(th);
    th->root_fiber = th->fiber = fib->cont.self;
    }
    

    这是它 fiber store 里面的实现,那么,确实,root fiber 是在第一个 fiber resume 的时候创建的

    static rb_fiber_t *
    root_fiber_alloc(rb_thread_t *th)
    {
        rb_fiber_t *fib;
        /* no need to allocate vm stack */
        fib = fiber_t_alloc(fiber_alloc(rb_cFiber));
        fib->cont.type = ROOT_FIBER_CONTEXT;
    #if FIBER_USE_NATIVE
    #ifdef _WIN32
        fib->fib_handle = ConvertThreadToFiber(0);
    #endif
    #endif
        fib->status = RUNNING;
        fib->prev_fiber = fib->next_fiber = fib;
    
        return fib;
    }
    

    在 windows 上会有非常明显的 ConvertThreadToFiber 的调用,因次,我才说会进入一个主 fiber 的模式

    3.我试过,兼容性不太好.....

  • #1 楼 @watraludru 感谢您的指出,不过这几点我的理解和您不太一致:

    1. 这里的并行是指逻辑上的并行,并不是指所有代码上在物理上的同一个时间上运行
    2. require 'fiber'是必须的,进入 irb 状态之后,可以访问到 Fiber 的库,但是 Fiber 的整个运行时态其实是未初始化的,你没办法访问到Fiber.current的方法,当require 'fiber'了之后,它做了以下事情: ```c

    void ruby_Init_Fiber_as_Coroutine(void);

    void Init_fiber(void) { ruby_Init_Fiber_as_Coroutine(); }

    
    见ruby-2.1.0源代码下`ext/fiber/fiber.c `
    
    ruby_Init_Fiber_as_Coroutine具体是这么实现的:
    ```c
    void
    ruby_Init_Fiber_as_Coroutine(void)
    {
        rb_define_method(rb_cFiber, "transfer", rb_fiber_m_transfer, -1);
        rb_define_method(rb_cFiber, "alive?", rb_fiber_alive_p, 0);
        rb_define_singleton_method(rb_cFiber, "current", rb_fiber_s_current, 0);
    }
    

    见 ruby-2.1.0 源代码下 cont.c 代码的 1686 行

    主 Fiber 这个概念是有的,ruby 中的在某个线程中,如果 require 了 fiber,那么当你在调用其它 fiber 的 resume 方法的时候,当前的线程模式就会转换成 fiber 模式,当前线程的代码堆栈就会成为你当前线程的 root_fiber

    VALUE
    rb_fiber_current(void)
    {
        rb_thread_t *th = GET_THREAD();
        if (th->fiber == 0) { // 就是这行
        /* save root */
        rb_fiber_t *fib = root_fiber_alloc(th);
        th->root_fiber = th->fiber = fib->cont.self;
        }
        return th->fiber;
    }
    

    见 ruby-2.1.0 源代码下 cont.c 代码的 1333 行

    第三个问题: em-synchrony 我通读过所有的代码,但是 fiber 问题不在于 em-synchrony 下简单的实现,而是在于需要定制化你所需求的库的读写,一味将 socket 直接指向 em-synchrony 下的 socket 实现不是一个兼容性非常好的问题

  • 报个名,准备去